Skip to content

Commit

Permalink
fix(assert): Map, Set and Symbol aren't parsed (#405)
Browse files Browse the repository at this point in the history
* fix(assert): `Map`, `Set` and `Symbol` aren't parsed

* chore: improve coverage

* ci: fix Deno imports

* ci: fix sep

* ci: fix sep

* ci: fix sep
  • Loading branch information
wellwelwel committed Jun 17, 2024
1 parent 1c1305f commit 9ab7202
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 144 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"test:deno:parallel": "tsx src/bin/index.ts --platform=\"deno\" --deno-allow=\"all\" --deno-cjs --parallel --include=\"ci/test/unit,ci/test/integration,ci/test/e2e\"",
"test:c8:sequential": "c8 tsx src/bin/index.ts --include=\"test/unit,test/integration,test/e2e\"",
"test:c8:parallel": "c8 tsx src/bin/index.ts --parallel --include=\"test/unit,test/integration,test/e2e\"",
"test:c8:sequential:options": "c8 tsx src/bin/index.ts --fast-fail --debug --exclude=\".bak\" --kill-port=\"4000\" --kill-range=\"4000-4001\" --include=\"test/unit,test/integration,test/e2e\"",
"test:c8:parallel:options": "c8 tsx src/bin/index.ts --parallel --concurrency=\"4\" --fast-fail --debug --exclude=\".bak\" --kill-port=\"4000\" --kill-range=\"4000-4001\" --include=\"test/unit,test/integration,test/e2e\"",
"test:c8:sequential:options": "c8 tsx src/bin/index.ts --platform=\"node\" --fast-fail --debug --exclude=\".bak\" --kill-port=\"4000\" --kill-range=\"4000-4001\" --include=\"test/unit,test/integration,test/e2e\" --filter=\".test.|.spec.\"",
"test:c8:parallel:options": "c8 tsx src/bin/index.ts --parallel --concurrency=\"4\" --platform=\"node\" --fast-fail --debug --exclude=\".bak\" --kill-port=\"4000\" --kill-range=\"4000-4001\" --include=\"test/unit,test/integration,test/e2e\" --filter=\".test.|.spec.\"",
"test:ci": "tsx ./test/ci.test.ts",
"test:ci:node": "FILTER='node-' npm run test:ci",
"test:ci:bun": "FILTER='bun-' npm run test:ci",
Expand Down
17 changes: 15 additions & 2 deletions src/helpers/parse-assertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,25 @@ const cwd = process.cwd();

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

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

if (Array.isArray(value)) return value.map(recurse);
if (value instanceof Set) return Array.from(value).map(recurse);
/* c8 ignore start */
if (value instanceof Map)
return recurse(
!nodeVersion || nodeVersion >= 12
? Object.fromEntries(value)
: fromEntries(value)
);
/* c8 ignore stop */

/* c8 ignore start */
if (value !== null && typeof value === 'object') {
Expand Down Expand Up @@ -66,15 +75,19 @@ export const parseAssertion = async (
try {
if (typeof each.before.cb === 'function' && each.before.assert) {
const beforeResult = each.before.cb();
/* c8 ignore next */
if (beforeResult instanceof Promise) await beforeResult;
/* c8 ignore next */
}

const cbResult = cb();
if (cbResult instanceof Promise) await cbResult;

if (typeof each.after.cb === 'function' && each.after.assert) {
const afterResult = each.after.cb();
/* c8 ignore next */
if (afterResult instanceof Promise) await afterResult;
/* c8 ignore next */
}

if (typeof options.message === 'string') {
Expand Down
3 changes: 3 additions & 0 deletions src/modules/describe.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* c8 ignore next */
import process from 'node:process';
import { format, backgroundColor } from '../helpers/format.js';
import { write } from '../helpers/logs.js';
Expand All @@ -6,6 +7,7 @@ import { indentation } from '../configs/indentation.js';
/* c8 ignore next */
import type { DescribeOptions } from '../@types/describe.js';

/* c8 ignore start */
/**
* On **Poku**, `describe` also can be used just as a pretty `console.log` to title your test suites in the terminal.
*/
Expand All @@ -21,6 +23,7 @@ export async function describe(
arg1: string | (() => unknown | Promise<unknown>),
arg2?: (() => unknown | Promise<unknown>) | DescribeOptions
): Promise<void> {
/* c8 ignore stop */
let title: string | undefined;
let cb: (() => unknown | Promise<unknown>) | undefined;
let options: DescribeOptions | undefined;
Expand Down
3 changes: 3 additions & 0 deletions src/modules/it.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* c8 ignore next */
import process from 'node:process';
/* c8 ignore next */
import { each } from '../configs/each.js';
Expand All @@ -6,6 +7,7 @@ import { indentation } from '../configs/indentation.js';
import { format } from '../helpers/format.js';
import { write } from '../helpers/logs.js';

/* c8 ignore start */
export async function it(
message: string,
cb: () => Promise<unknown>
Expand All @@ -19,6 +21,7 @@ export async function it(
(() => unknown | Promise<unknown>)?,
]
): Promise<void> {
/* c8 ignore stop */
let message: string | undefined;
let cb: () => unknown | Promise<unknown>;

Expand Down
163 changes: 61 additions & 102 deletions src/modules/list-files.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* c8 ignore next */
import process from 'node:process';
import { readdir, stat as fsStat } from 'node:fs';
import { sep, join } from 'node:path';
import { readdir, stat as fsStat } from '../polyfills/fs.js';
/* c8 ignore next */
import type { Configs } from '../@types/list-files.js';

Expand All @@ -10,121 +11,79 @@ export const sanitizePath = (input: string, ensureTarget?: boolean): string => {
.replace(/(\.\.(\/|\\|$))+/g, '') // ensure the current path level
.replace(/[<>|^?*]+/g, ''); // removing unusual path characters

return ensureTarget ? sanitizedPath.replace(/^[/\\]/, './') : sanitizedPath;
// Preventing absolute path access
return ensureTarget
? sanitizedPath.replace(/^[/\\]/, `.${sep}`)
: sanitizedPath;
};

/* c8 ignore start */
export const isFile = async (fullPath: string) =>
(await fsStat(fullPath)).isFile();
/* c8 ignore stop */

/* c8 ignore start */
export const escapeRegExp = (string: string) =>
string.replace(/[.*{}[\]\\]/g, '\\$&');
/* c8 ignore stop */

export const isFile = (fullPath: string): Promise<boolean> => {
return new Promise((resolve, reject) => {
fsStat(fullPath, (err, stats) => {
/* c8 ignore next */
if (err) return reject(err);

resolve(stats.isFile());
});
});
};

/* c8 ignore start */
const envFilter = process.env.FILTER?.trim()
? new RegExp(escapeRegExp(process.env.FILTER), 'i')
: undefined;
/* c8 ignore stop */

const getAllFiles = (
export const getAllFiles = async (
dirPath: string,
files: string[] = [],
configs?: Configs,
callback?: (err: NodeJS.ErrnoException | null, result?: string[]) => void
) => {
readdir(sanitizePath(dirPath), (err, currentFiles) => {
if (err) return callback?.(err);

const defaultRegExp = /\.(test|spec)\./i;
const filter: RegExp =
(envFilter
? envFilter
: configs?.filter instanceof RegExp
? configs.filter
: defaultRegExp) || defaultRegExp;

const exclude: Configs['exclude'] = configs?.exclude
? Array.isArray(configs.exclude)
? /* c8 ignore next */
configs.exclude
: [configs.exclude]
: undefined;

let pending = currentFiles.length;
if (!pending) return callback?.(null, files);

currentFiles.forEach((file) => {
configs?: Configs
): Promise<string[]> => {
const currentFiles = await readdir(sanitizePath(dirPath));
const defaultRegExp = /\.(test|spec)\./i;
/* c8 ignore start */
const filter: RegExp =
(envFilter
? envFilter
: configs?.filter instanceof RegExp
? configs.filter
: defaultRegExp) || defaultRegExp;

const exclude: Configs['exclude'] = configs?.exclude
? Array.isArray(configs.exclude)
? configs.exclude
: [configs.exclude]
: undefined;
/* c8 ignore stop */

await Promise.all(
currentFiles.map(async (file) => {
const fullPath = join(dirPath, file);

fsStat(fullPath, (err, stat) => {
/* c8 ignore start */
if (err) {
if (!--pending) callback?.(null, files);
return;
}
/* c8 ignore stop */

if (
fullPath.indexOf('node_modules') !== -1 ||
fullPath.indexOf('.git') === 0
) {
/* c8 ignore start */
if (!--pending) callback?.(null, files);
return;
/* c8 ignore stop */
}

if (exclude) {
for (let i = 0; i < exclude.length; i++) {
if (exclude[i].test(fullPath)) {
/* c8 ignore start */
if (!--pending) callback?.(null, files);
return;
/* c8 ignore stop */
}
}
}

if (filter.test(fullPath)) {
files.push(fullPath);

/* c8 ignore start */
if (!--pending) callback?.(null, files);
return;
/* c8 ignore stop */
const stat = await fsStat(fullPath);

/* c8 ignore start */
if (
fullPath.indexOf('node_modules') !== -1 ||
fullPath.indexOf('.git') === 0
)
return;
/* c8 ignore stop */

if (exclude) {
for (let i = 0; i < exclude.length; i++) {
/* c8 ignore next */
if (exclude[i].test(fullPath)) return;
}
}

if (stat.isDirectory()) {
getAllFiles(fullPath, files, configs, (err) => {
/* c8 ignore start */
if (err) {
if (!--pending) callback?.(err, files);
return;
}
/* c8 ignore stop */
if (filter.test(fullPath)) return files.push(fullPath);
if (stat.isDirectory()) await getAllFiles(fullPath, files, configs);
})
);

if (!--pending) callback?.(null, files);
});
/* c8 ignore next */
} else if (!--pending) callback?.(null, files);
});
});
});
return files;
};

export const listFiles = (
targetDir: string,
configs?: Configs
): Promise<string[]> =>
new Promise((resolve, reject) => {
getAllFiles(sanitizePath(targetDir), [], configs, (err, result) => {
if (err) return reject(err);

resolve(result!);
});
});
/* c8 ignore start */
export const listFiles = async (targetDir: string, configs?: Configs) =>
await getAllFiles(sanitizePath(targetDir), [], configs);
/* c8 ignore stop */
3 changes: 3 additions & 0 deletions src/modules/test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* c8 ignore next */
import process from 'node:process';
/* c8 ignore next */
import { each } from '../configs/each.js';
Expand All @@ -6,6 +7,7 @@ import { indentation } from '../configs/indentation.js';
import { format } from '../helpers/format.js';
import { write } from '../helpers/logs.js';

/* c8 ignore start */
export async function test(
message: string,
cb: () => Promise<unknown>
Expand All @@ -19,6 +21,7 @@ export async function test(
(() => unknown | Promise<unknown>)?,
]
): Promise<void> {
/* c8 ignore stop */
let message: string | undefined;
let cb: () => unknown | Promise<unknown>;

Expand Down
35 changes: 25 additions & 10 deletions src/polyfills/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,37 @@ import {
type Stats,
} from 'node:fs';

export const readdir = (
export function readdir(path: string): Promise<string[]>;
export function readdir(
path: string,
options: { withFileTypes: true }
): Promise<Dirent[]> =>
new Promise((resolve, reject) => {
nodeReaddir(path, options, (err, entries) => {
if (err) reject(err);
else resolve(entries);
): Promise<Dirent[]>;
export function readdir(
path: string,
options?: { withFileTypes?: boolean }
): Promise<string[] | Dirent[]> {
return new Promise((resolve, reject) => {
if (options?.withFileTypes) {
nodeReaddir(path, { withFileTypes: true }, (err, entries) => {
if (err) return reject(err);
resolve(entries);
});

return;
}

nodeReaddir(path, (err, files) => {
if (err) return reject(err);
resolve(files);
});
});
}

export const stat = (path: string): Promise<Stats> => {
return new Promise((resolve, reject) => {
nodeStat(path, (err, stats) => {
if (err) reject(err);
else resolve(stats);
if (err) return reject(err);
resolve(stats);
});
});
};
Expand All @@ -34,8 +49,8 @@ export const readFile = (
): Promise<string> =>
new Promise((resolve, reject) => {
nodeReadFile(path, encoding, (err, data) => {
if (err) reject(err);
else resolve(data);
if (err) return reject(err);
resolve(data);
});
});

Expand Down
6 changes: 4 additions & 2 deletions src/polyfills/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ export const entries = (obj: { [key: string]: any }): [string, unknown][] => {
};

export const fromEntries = (
entries: [string, unknown][]
entries: [string, unknown][] | Map<string, unknown>
): Record<string, unknown> => {
return entries.reduce(
const mappedEntries = entries instanceof Map ? Array.from(entries) : entries;

return mappedEntries.reduce(
(acc, [key, value]) => {
acc[key] = value;
return acc;
Expand Down
4 changes: 2 additions & 2 deletions src/services/run-tests.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import process from 'node:process';
import { EOL } from 'node:os';
import { join, relative } from 'node:path';
import { join, relative, sep } from 'node:path';
import { runner } from '../helpers/runner.js';
import { indentation } from '../configs/indentation.js';
import {
Expand Down Expand Up @@ -42,7 +42,7 @@ export const runTests = async (
if (showLogs) {
hr();
write(
`${format.bold(isFile ? 'File:' : 'Directory:')} ${format.underline(`./${currentDir}`)}${EOL}`
`${format.bold(isFile ? 'File:' : 'Directory:')} ${format.underline(`.${sep}${currentDir}`)}${EOL}`
);
}

Expand Down
Loading

0 comments on commit 9ab7202

Please sign in to comment.