Skip to content

Commit

Permalink
fix(assert): improve multi-depth logs (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
wellwelwel authored May 19, 2024
1 parent ea5ed38 commit f7abbfa
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 31 deletions.
3 changes: 3 additions & 0 deletions fixtures/sintax/big-int.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: BigInt literals are not available when targeting lower than ES2020
export const bigIntValue = 987456321456987456321n;
12 changes: 12 additions & 0 deletions src/@types/assert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* c8 ignore start */

export type ParseAssertionOptions = {
message?: string | Error;
defaultMessage?: string;
actual?: string;
expected?: string;
throw?: boolean;
hideDiff?: boolean;
};

/* c8 ignore stop */
66 changes: 43 additions & 23 deletions src/helpers/parseAsssetion.ts → src/helpers/parse-assertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,44 @@ import { format } from './format.js';
import { hr } from './hr.js';
import { findFile } from './find-file.js';
import { each } from '../configs/each.js';
import { fromEntries, entries } from '../polyfills/object.js';
/* c8 ignore next */
import { ParseAssertionOptions } from '../@types/assert.js';
import { nodeVersion } from './get-runtime.js';

export const parseResultType = (type?: unknown): string => {
const recurse = (value: unknown): unknown => {
if (typeof value === 'undefined') return 'undefined';

if (
typeof value === 'function' ||
typeof value === 'bigint' ||
value instanceof RegExp
)
return String(value);

if (Array.isArray(value)) return value.map(recurse);

/* c8 ignore start */
if (value !== null && typeof value === 'object') {
if (!nodeVersion || nodeVersion >= 12)
return Object.fromEntries(
Object.entries(value).map(([key, val]) => [key, recurse(val)])
);

return fromEntries(
entries(value).map(([key, val]) => [key, recurse(val)])
);
}
/* c8 ignore stop */

return value;
};

/* c8 ignore start */
export type ParseAssertionOptions = {
message?: string | Error;
defaultMessage?: string;
actual?: string;
expected?: string;
throw?: boolean;
hideDiff?: boolean;
const result = recurse(type);

return typeof result === 'string' ? result : JSON.stringify(result, null, 2);
};
/* c8 ignore stop */

export const parseAssertion = async (
cb: () => void | Promise<void>,
Expand Down Expand Up @@ -78,26 +105,19 @@ export const parseAssertion = async (
console.log(`${format.dim(' Operator')} ${operator}${EOL}`);

if (!options?.hideDiff) {
const splitActual = parseResultType(actual).split('\n');
const splitExpected = parseResultType(expected).split('\n');

console.log(format.dim(` ${options?.actual || 'Actual'}:`));
console.log(
format.bold(
typeof actual === 'function' || actual instanceof RegExp
? ` ${String(actual)}`
: ` ${format.fail(JSON.stringify(actual))}`
)
splitActual.forEach((line) =>
console.log(` ${format.bold(format.fail(line))}`)
);

console.log(
`${EOL} ${format.dim(`${options?.expected || 'Expected'}:`)}`
);
console.log(
format.bold(
`${
typeof expected === 'function' || expected instanceof RegExp
? ` ${String(expected)}`
: ` ${format.success(JSON.stringify(expected))}`
}`
)
splitExpected.forEach((line) =>
console.log(` ${format.bold(format.success(line))}`)
);
}

Expand Down
7 changes: 3 additions & 4 deletions src/modules/assert-promise.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import * as nodeAssert from 'node:assert';
import {
parseAssertion,
ParseAssertionOptions,
} from '../helpers/parseAsssetion.js';
import { parseAssertion } from '../helpers/parse-assertion.js';
import { nodeVersion } from '../helpers/get-runtime.js';
/* c8 ignore next */
import { ParseAssertionOptions } from '../@types/assert.js';

const ok = async (
value: unknown,
Expand Down
7 changes: 3 additions & 4 deletions src/modules/assert.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import * as nodeAssert from 'node:assert';
import {
parseAssertion,
ParseAssertionOptions,
} from '../helpers/parseAsssetion.js';
import { parseAssertion } from '../helpers/parse-assertion.js';
import { nodeVersion } from '../helpers/get-runtime.js';
/* c8 ignore next */
import { ParseAssertionOptions } from '../@types/assert.js';

const ok = (
value: unknown,
Expand Down
27 changes: 27 additions & 0 deletions src/polyfills/object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* c8 ignore start */

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const entries = (obj: { [key: string]: any }): [string, unknown][] => {
const ownProps = Object.keys(obj);
let i = ownProps.length;
const resArray = new Array(i);

// benchmark `while` outperformed `for`
while (i--) resArray[i] = [ownProps[i], obj[ownProps[i]]];

return resArray;
};

export const fromEntries = (
entries: [string, unknown][]
): Record<string, unknown> => {
return entries.reduce(
(acc, [key, value]) => {
acc[key] = value;
return acc;
},
{} as Record<string, unknown>
);
};

/* c8 ignore stop */
143 changes: 143 additions & 0 deletions test/unit/assert.result-type.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { test, assert, describe } from '../../src/index.js';
import { parseResultType } from '../../src/helpers/parse-assertion.js';
import { nodeVersion } from '../../src/helpers/get-runtime.js';

describe('Assert: Parse Result Type', { background: false, icon: '🔬' });
test(async () => {
assert.deepStrictEqual(
parseResultType(),
'undefined',
'Undefined (Implicit)'
);
assert.deepStrictEqual(
parseResultType(undefined),
'undefined',
'Undefined (Explicit)'
);
assert.deepStrictEqual(parseResultType(null), 'null', 'Null');
assert.deepStrictEqual(parseResultType(true), 'true', 'True');
assert.deepStrictEqual(parseResultType(false), 'false', 'False');
assert.deepStrictEqual(parseResultType('string'), 'string', 'String');
assert.deepStrictEqual(
parseResultType(`
Multi
Line
`),
`
Multi
Line
`,
'String (Multi Line/Table)'
);
assert.deepStrictEqual(parseResultType(123), '123', 'Number');
if (!nodeVersion || nodeVersion >= 10) {
const module = await import('../../fixtures/sintax/big-int.js');
assert.deepStrictEqual(
parseResultType(module.bigIntValue),
'987456321456987456321',
'Big Int'
);
}
assert.deepStrictEqual(parseResultType(/123/), '/123/', 'Regex');

assert(/=>/.test(parseResultType(() => {})), 'Anonymous Function');
assert(
/=>/.test(parseResultType((a: number) => a)),
'Anonymous Function (Param)'
);
assert(
/=>/.test(parseResultType((a: number, b: number) => a + b)),
'Anonymous Function (Params)'
);

assert(
/function/.test(
parseResultType(function () {
return;
})
),
'Function'
);
assert(
/function/.test(
parseResultType(function (a: number) {
return a;
})
),
'Function (Param)'
);
assert(
/function/.test(
parseResultType(function (a: number, b: number) {
return a + b;
})
),
'Function (Params)'
);

assert.deepStrictEqual(
parseResultType({ a: true }),
`{
"a": true
}`,
'Object'
);
assert.deepStrictEqual(parseResultType({}), '{}', 'Object (Empty)');
assert.deepStrictEqual(
parseResultType({ a: { b: 123 }, c: [/123/gi] }),
`{
"a": {
"b": 123
},
"c": [
"/123/gi"
]
}`,
'Object (Complex)'
);
});
assert.deepStrictEqual(
parseResultType([1]),
`[
1
]`,
'Array'
);
assert.deepStrictEqual(parseResultType([]), `[]`, 'Array (Empty)');
assert.deepStrictEqual(
parseResultType([
1,
true,
undefined,
/123/gm,
{ a: { b: [{ c: undefined }] } },
[[[[/[^0-9]/]]]],
]),
`[
1,
true,
"undefined",
"/123/gm",
{
"a": {
"b": [
{
"c": "undefined"
}
]
}
},
[
[
[
[
"/[^0-9]/"
]
]
]
]
]`,
'Array Complex'
);

0 comments on commit f7abbfa

Please sign in to comment.