Skip to content

Instantly share code, notes, and snippets.

@webbower
Last active June 20, 2024 21:28
Show Gist options
  • Save webbower/668179303546eb53faba70951d30c68c to your computer and use it in GitHub Desktop.
Save webbower/668179303546eb53faba70951d30c68c to your computer and use it in GitHub Desktop.
Interviewing utilities
// START: DENO STD colors
// https://jsr.io/@std/fmt/doc
/**
* Builds color code
* @param open
* @param close
*/
const code = (open, close) => ({
open: `\x1b[${open.join(';')}m`,
close: `\x1b[${close}m`,
regexp: new RegExp(`\\x1b\\[${close}m`, 'g'),
});
const run = (str, code) => `${code.open}${str.replace(code.regexp, code.open)}${code.close}`;
const bold = str => run(str, code([1], 22));
const yellow = str => run(str, code([33], 39));
const red = str => run(str, code([31], 39));
const green = str => run(str, code([32], 39));
// END: DENO STD colors
// START: Deno STD Deep equality comparison
function isKeyedCollection(x) {
return [Symbol.iterator, 'size'].every(k => k in x);
}
function constructorsEqual(a, b) {
return (
a.constructor === b.constructor ||
(a.constructor === Object && !b.constructor) ||
(!a.constructor && b.constructor === Object)
);
}
/**
* Deep equality comparison used in assertions
*
* @param {unknown} c The actual value
* @param {unknown} d The expected value
* @returns {boolean} `true` if the values are deeply equal, `false` otherwise
*
* @example Usage
* ```ts
* import { equal } from "@std/assert/equal";
*
* equal({ foo: "bar" }, { foo: "bar" }); // Returns `true`
* equal({ foo: "bar" }, { foo: "baz" }); // Returns `false
* ```
*
* @see https://jsr.io/@std/assert/0.226.0/equal.ts
*/
function isEqual(c, d) {
const seen = new Map();
return (function compare(a, b) {
// Have to render RegExp & Date for string comparison
// unless it's mistreated as object
if (
a &&
b &&
((a instanceof RegExp && b instanceof RegExp) || (a instanceof URL && b instanceof URL))
) {
return String(a) === String(b);
}
if (a instanceof Date && b instanceof Date) {
const aTime = a.getTime();
const bTime = b.getTime();
// Check for NaN equality manually since NaN is not
// equal to itself.
if (Number.isNaN(aTime) && Number.isNaN(bTime)) {
return true;
}
return aTime === bTime;
}
if (typeof a === 'number' && typeof b === 'number') {
return (Number.isNaN(a) && Number.isNaN(b)) || a === b;
}
if (Object.is(a, b)) {
return true;
}
if (a && typeof a === 'object' && b && typeof b === 'object') {
if (a && b && !constructorsEqual(a, b)) {
return false;
}
if (a instanceof WeakMap || b instanceof WeakMap) {
if (!(a instanceof WeakMap && b instanceof WeakMap)) return false;
throw new TypeError('cannot compare WeakMap instances');
}
if (a instanceof WeakSet || b instanceof WeakSet) {
if (!(a instanceof WeakSet && b instanceof WeakSet)) return false;
throw new TypeError('cannot compare WeakSet instances');
}
if (a instanceof WeakRef || b instanceof WeakRef) {
if (!(a instanceof WeakRef && b instanceof WeakRef)) return false;
return compare(a.deref(), b.deref());
}
if (seen.get(a) === b) {
return true;
}
if (Object.keys(a).length !== Object.keys(b).length) {
return false;
}
seen.set(a, b);
if (isKeyedCollection(a) && isKeyedCollection(b)) {
if (a.size !== b.size) {
return false;
}
let unmatchedEntries = a.size;
for (const [aKey, aValue] of a.entries()) {
for (const [bKey, bValue] of b.entries()) {
/* Given that Map keys can be references, we need
* to ensure that they are also deeply equal */
if (
(aKey === aValue && bKey === bValue && compare(aKey, bKey)) ||
(compare(aKey, bKey) && compare(aValue, bValue))
) {
unmatchedEntries--;
break;
}
}
}
return unmatchedEntries === 0;
}
const merged = { ...a, ...b };
for (const key of [
...Object.getOwnPropertyNames(merged),
...Object.getOwnPropertySymbols(merged),
]) {
if (!compare(a && a[key], b && b[key])) {
return false;
}
if ((key in a && !(key in b)) || (key in b && !(key in a))) {
return false;
}
}
return true;
}
return false;
})(c, d);
}
// END: Deno STD Deep equality comparison
/**
* List of test outcomes
* @type {[('PASS' | 'FAIL'), string][]}
*/
const outcomes = [];
/**
* Write the test outcomes to the console
*/
const writeTestOutcomes = () => {
outcomes.forEach(([status, message], i) => {
if (status === 'PASS') {
console.info(green(`# ${i + 1} ok: ${message}`));
} else {
console.error(red(`# ${i + 1} not ok: ${message}`));
}
});
};
/**
* @template {T}
* @typedef {Object} AssertFnConfig The config object arg for the `assert()` function
* @prop {string} given A description of the context for the test(s)
* @prop {string} should A description of the expected outcome
* @prop {T} actual The result of the executed test case
* @prop {T} expected The expected result of the executed test case
*/
const requiredKeys = ['given', 'should', 'actual', 'expected'];
/**
* Perform strict equals assertion for primitive values or deep equals assertion for object values
*
* @param {AssertFnConfig} config
* @returns {void}
*/
const assert = config => {
const missingKeys = requiredKeys.filter(rk => !Object.hasOwn(config, rk));
if (missingKeys.length) {
throw new TypeError(
`\`assert()\` first argument is missing the following keys: ${missingKeys.join(', ')}`
);
}
const { given, should, actual, expected } = config;
const message = `given ${given}; should ${should}`;
outcomes.push([isEqual(actual, expected) ? 'PASS' : 'FAIL', message]);
};
/**
* Define a test block
*
* @param {string} title The title of the test block
* @param {(assert: AssertFnConfig) => void} testBlock The function wrapper in which to perform assertions
* @returns {void}
*/
const describe = (title, testBlock) => {
console.info(bold(yellow(`### ${title}`)));
try {
testBlock(assert);
} catch (error) {
console.error(red(`There was an unexpected error: ${error.message}`));
console.error(red(error.stack));
}
};
////// START TESTS
////// END TESTS
writeTestOutcomes();
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
/* Button reset adapted from https://css-tricks.com/overriding-default-button-styles/ */
a[class^="button"],
button {
border: 0;
font-family: system-ui, sans-serif;
font-size: 1rem;
line-height: 1.0;
white-space: nowrap;
text-decoration: none;
margin: 0;
cursor: pointer;
}
button {
background: transparent;
padding: 0;
}
a[class^="button"] {
display: inline-block;
}
.button-primary {
border-radius: 0.25rem;
background: #1E88E5;
color: white;
padding: 0.25rem 0.5rem;
}
/*
* Hide only visually, but have it available for screen readers:
* https://snook.ca/archives/html_and_css/hiding-content-for-accessibility
*
* 1. For long content, line feeds are not interpreted as spaces and small width
* causes content to wrap 1 word per line:
* https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe
*/
.vh {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
/* 1 */
}
/*
* Extends the .vh class to allow the element
* to be focusable when navigated to via the keyboard:
* https://www.drupal.org/node/897638
*/
.vh.focusable:active,
.vh.focusable:focus {
clip: auto;
height: auto;
margin: 0;
overflow: visible;
position: static;
white-space: inherit;
width: auto;
}
// https://medium.com/javascript-scene/abstract-data-types-and-the-software-crisis-671ea7fc72e7
// With modifications
const assert = ({ given, should, actual, expected }) => {
const stringify = (value) =>
Array.isArray(value)
? `[${value.map(stringify).join(",")}]`
: `${JSON.stringify(value)}`;
const actualString = stringify(actual);
const expectedString = stringify(expected);
if (actualString === expectedString) {
console.log(`OK:
given: ${given}
should: ${should}
actual: ${actualString}
expected: ${expectedString}
`);
} else {
throw new Error(`NOT OK:
given ${given}
should ${should}
actual: ${actualString}
expected: ${expectedString}
`);
}
};
const compose = (...fns) => x => fns.reduceRight((memo, fn) => fn(memo), x);
const pipe = (...fns) => x => fns.reduce((memo, fn) => fn(memo), x);
const curry = (f, arr = []) => (...args) => (a => a.length === f.length ? f(...a) : curry(f, a))([...arr, ...args]);
const identity = x => x;
const noop = () => {};
const prop = curry((key, obj) => obj[key]);
const map = curry((fn, mappable) => mappable.map(fn));
const filter = curry((pred, filterable) => filterable.filter(pred));
const inc = (x) => x + 1;
const double = (x) => x * 2;
const isString = (x) => typeof x === "string";
console.log("compose(inc, double)(2)", compose(inc, double)(2), "=== 5");
console.log("pipe(inc, double)(2)", pipe(inc, double)(2), " === 6");
console.log("identity('a')", identity("a"), "=== a");
console.log("noop()", noop(1), "=== undefined");
console.log("prop('a', {a: 'a'})", prop("a", { a: "a" }), "=== a");
console.log("prop('a')({a: 'a'})", prop("a")({ a: "a" }), "=== a");
console.log("map(inc, [1, 2, 3])", map(inc, [1, 2, 3]), "=== [2,3,4]");
console.log("map(inc)([1, 2, 3])", map(inc)([1, 2, 3]), "=== [2,3,4]");
console.log("filter(isString, [1, 'a', 2, 'b', 3])", filter(isString, [1, "a", 2, "b", 3]), "=== [a, b]");
console.log("filter(isString)([1, 'a', 2, 'b', 3])", filter(isString)([1, "a", 2, "b", 3]), "=== [a, b]");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment