From c073ed365d3998c2ca373976af946a3cf6dbfa89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Weslley=20Ara=C3=BAjo?= <46850407+wellwelwel@users.noreply.github.com> Date: Wed, 26 Jun 2024 08:04:22 -0300 Subject: [PATCH] fix: recognize `.mts` and `.cts` extensions as TS files (#474) --- .github/workflows/bot_deps-docs-update.yml | 2 +- .github/workflows/bot_deps-update.yml | 2 +- .github/workflows/cd_docs-deploy.yml | 2 +- .github/workflows/cd_publish.yml | 2 +- .github/workflows/ci_benchmark.yml | 2 +- .github/workflows/ci_compatibility-bun.yml | 2 +- .github/workflows/ci_compatibility-deno.yml | 2 +- .github/workflows/ci_compatibility-nodejs.yml | 2 +- .github/workflows/ci_coverage-linux.yml | 48 +++++- .github/workflows/ci_coverage-osx.yml | 8 +- .github/workflows/ci_coverage-windows.yml | 8 +- .github/workflows/ci_docs-website.yml | 2 +- .github/workflows/ci_lint.yml | 2 +- biome.json | 6 + fixtures/extensions/spec/test.spec.cjs | 0 fixtures/extensions/spec/test.spec.cts | 0 fixtures/extensions/spec/test.spec.js | 0 fixtures/extensions/spec/test.spec.mjs | 0 fixtures/extensions/spec/test.spec.mts | 0 fixtures/extensions/spec/test.spec.ts | 0 fixtures/extensions/test/test.test.cjs | 0 fixtures/extensions/test/test.test.cts | 0 fixtures/extensions/test/test.test.js | 0 fixtures/extensions/test/test.test.mjs | 0 fixtures/extensions/test/test.test.mts | 0 fixtures/extensions/test/test.test.ts | 0 src/helpers/find-file.ts | 6 +- src/helpers/force-array.ts | 8 - src/helpers/get-arg.ts | 39 +++-- src/helpers/get-runtime.ts | 11 +- src/helpers/logs.ts | 9 +- src/helpers/runner.ts | 8 +- src/helpers/time.ts | 4 +- src/modules/exit.ts | 4 +- src/modules/list-files-sync.ts | 6 +- src/services/map-tests.ts | 6 +- src/services/pid.ts | 9 +- src/services/watch.ts | 3 +- test/e2e/runners.test.ts | 90 +++++++++++ test/helpers/capture-cli.test.ts | 34 ++++- test/integration/describe/describe.test.ts | 5 + test/unit/args.test.ts | 73 +++++++++ test/unit/assert-find-file.test.ts | 25 +++- test/unit/logs.test.ts | 141 ++++++++++++++++++ 44 files changed, 497 insertions(+), 74 deletions(-) create mode 100644 fixtures/extensions/spec/test.spec.cjs create mode 100644 fixtures/extensions/spec/test.spec.cts create mode 100644 fixtures/extensions/spec/test.spec.js create mode 100644 fixtures/extensions/spec/test.spec.mjs create mode 100644 fixtures/extensions/spec/test.spec.mts create mode 100644 fixtures/extensions/spec/test.spec.ts create mode 100644 fixtures/extensions/test/test.test.cjs create mode 100644 fixtures/extensions/test/test.test.cts create mode 100644 fixtures/extensions/test/test.test.js create mode 100644 fixtures/extensions/test/test.test.mjs create mode 100644 fixtures/extensions/test/test.test.mts create mode 100644 fixtures/extensions/test/test.test.ts delete mode 100644 src/helpers/force-array.ts create mode 100644 test/e2e/runners.test.ts create mode 100644 test/unit/args.test.ts create mode 100644 test/unit/logs.test.ts diff --git a/.github/workflows/bot_deps-docs-update.yml b/.github/workflows/bot_deps-docs-update.yml index 352cecd8..8e1f7993 100644 --- a/.github/workflows/bot_deps-docs-update.yml +++ b/.github/workflows/bot_deps-docs-update.yml @@ -24,7 +24,7 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' diff --git a/.github/workflows/bot_deps-update.yml b/.github/workflows/bot_deps-update.yml index a9abe6a6..521141f1 100644 --- a/.github/workflows/bot_deps-update.yml +++ b/.github/workflows/bot_deps-update.yml @@ -23,7 +23,7 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' diff --git a/.github/workflows/cd_docs-deploy.yml b/.github/workflows/cd_docs-deploy.yml index c76d94af..fadf8911 100644 --- a/.github/workflows/cd_docs-deploy.yml +++ b/.github/workflows/cd_docs-deploy.yml @@ -17,7 +17,7 @@ jobs: - name: Actions - Checkout uses: actions/checkout@v4 - - name: Actions - Setup NodeJS + - name: Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' diff --git a/.github/workflows/cd_publish.yml b/.github/workflows/cd_publish.yml index 9ad3c534..a86a2722 100644 --- a/.github/workflows/cd_publish.yml +++ b/.github/workflows/cd_publish.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v4 if: ${{ steps.release.outputs.releases_created }} - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 if: ${{ steps.release.outputs.releases_created }} with: diff --git a/.github/workflows/ci_benchmark.yml b/.github/workflows/ci_benchmark.yml index 1787b6d6..f233c2e4 100644 --- a/.github/workflows/ci_benchmark.yml +++ b/.github/workflows/ci_benchmark.yml @@ -15,7 +15,7 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' diff --git a/.github/workflows/ci_compatibility-bun.yml b/.github/workflows/ci_compatibility-bun.yml index d9f75df4..ac560ed0 100644 --- a/.github/workflows/ci_compatibility-bun.yml +++ b/.github/workflows/ci_compatibility-bun.yml @@ -17,7 +17,7 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' diff --git a/.github/workflows/ci_compatibility-deno.yml b/.github/workflows/ci_compatibility-deno.yml index a3df55ae..3eb3da87 100644 --- a/.github/workflows/ci_compatibility-deno.yml +++ b/.github/workflows/ci_compatibility-deno.yml @@ -17,7 +17,7 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' diff --git a/.github/workflows/ci_compatibility-nodejs.yml b/.github/workflows/ci_compatibility-nodejs.yml index 7bc1dc3f..8fd232dc 100644 --- a/.github/workflows/ci_compatibility-nodejs.yml +++ b/.github/workflows/ci_compatibility-nodejs.yml @@ -36,7 +36,7 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' diff --git a/.github/workflows/ci_coverage-linux.yml b/.github/workflows/ci_coverage-linux.yml index 447592e3..23f6cb96 100644 --- a/.github/workflows/ci_coverage-linux.yml +++ b/.github/workflows/ci_coverage-linux.yml @@ -18,11 +18,21 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' + - name: ➕ Actions - Setup Bun + uses: oven-sh/setup-bun@v1 + with: + bun-version: 'latest' + + - name: ➕ Actions - Setup Deno + uses: denoland/setup-deno@v1 + with: + deno-version: 'v1.x' + - name: ➕ Cache dependencies uses: actions/cache@v4 with: @@ -53,11 +63,21 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' + - name: ➕ Actions - Setup Bun + uses: oven-sh/setup-bun@v1 + with: + bun-version: 'latest' + + - name: ➕ Actions - Setup Deno + uses: denoland/setup-deno@v1 + with: + deno-version: 'v1.x' + - name: ➕ Cache dependencies uses: actions/cache@v4 with: @@ -88,11 +108,21 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' + - name: ➕ Actions - Setup Bun + uses: oven-sh/setup-bun@v1 + with: + bun-version: 'latest' + + - name: ➕ Actions - Setup Deno + uses: denoland/setup-deno@v1 + with: + deno-version: 'v1.x' + - name: ➕ Cache dependencies uses: actions/cache@v4 with: @@ -123,11 +153,21 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' + - name: ➕ Actions - Setup Bun + uses: oven-sh/setup-bun@v1 + with: + bun-version: 'latest' + + - name: ➕ Actions - Setup Deno + uses: denoland/setup-deno@v1 + with: + deno-version: 'v1.x' + - name: ➕ Cache dependencies uses: actions/cache@v4 with: diff --git a/.github/workflows/ci_coverage-osx.yml b/.github/workflows/ci_coverage-osx.yml index 2324fc9d..867b87cd 100644 --- a/.github/workflows/ci_coverage-osx.yml +++ b/.github/workflows/ci_coverage-osx.yml @@ -25,7 +25,7 @@ jobs: key: npm-osx-${{ hashFiles('package-lock.json') }} restore-keys: npm-osx- - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' @@ -60,7 +60,7 @@ jobs: key: npm-osx-${{ hashFiles('package-lock.json') }} restore-keys: npm-osx- - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' @@ -95,7 +95,7 @@ jobs: key: npm-osx-${{ hashFiles('package-lock.json') }} restore-keys: npm-osx- - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' @@ -130,7 +130,7 @@ jobs: key: npm-osx-${{ hashFiles('package-lock.json') }} restore-keys: npm-osx- - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' diff --git a/.github/workflows/ci_coverage-windows.yml b/.github/workflows/ci_coverage-windows.yml index 869a04ec..991e4e51 100644 --- a/.github/workflows/ci_coverage-windows.yml +++ b/.github/workflows/ci_coverage-windows.yml @@ -18,7 +18,7 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' @@ -47,7 +47,7 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' @@ -76,7 +76,7 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' @@ -105,7 +105,7 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' diff --git a/.github/workflows/ci_docs-website.yml b/.github/workflows/ci_docs-website.yml index bf8f2dc8..61c010df 100644 --- a/.github/workflows/ci_docs-website.yml +++ b/.github/workflows/ci_docs-website.yml @@ -15,7 +15,7 @@ jobs: - name: Actions - Checkout uses: actions/checkout@v4 - - name: Actions - Setup NodeJS + - name: Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' diff --git a/.github/workflows/ci_lint.yml b/.github/workflows/ci_lint.yml index 474df9e2..d02e836e 100644 --- a/.github/workflows/ci_lint.yml +++ b/.github/workflows/ci_lint.yml @@ -18,7 +18,7 @@ jobs: - name: ➕ Actions - Checkout uses: actions/checkout@v4 - - name: ➕ Actions - Setup NodeJS + - name: ➕ Actions - Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.x' diff --git a/biome.json b/biome.json index 0fbca6d8..79cb67d2 100644 --- a/biome.json +++ b/biome.json @@ -75,6 +75,9 @@ "include": ["src/polyfills/**/**"], "linter": { "rules": { + "style": { + "noNonNullAssertion": "off" + }, "suspicious": { "noExplicitAny": "off" } @@ -85,6 +88,9 @@ "include": ["src/index.ts"], "linter": { "rules": { + "style": { + "noNonNullAssertion": "off" + }, "performance": { "noBarrelFile": "off" } diff --git a/fixtures/extensions/spec/test.spec.cjs b/fixtures/extensions/spec/test.spec.cjs new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/extensions/spec/test.spec.cts b/fixtures/extensions/spec/test.spec.cts new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/extensions/spec/test.spec.js b/fixtures/extensions/spec/test.spec.js new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/extensions/spec/test.spec.mjs b/fixtures/extensions/spec/test.spec.mjs new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/extensions/spec/test.spec.mts b/fixtures/extensions/spec/test.spec.mts new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/extensions/spec/test.spec.ts b/fixtures/extensions/spec/test.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/extensions/test/test.test.cjs b/fixtures/extensions/test/test.test.cjs new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/extensions/test/test.test.cts b/fixtures/extensions/test/test.test.cts new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/extensions/test/test.test.js b/fixtures/extensions/test/test.test.js new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/extensions/test/test.test.mjs b/fixtures/extensions/test/test.test.mjs new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/extensions/test/test.test.mts b/fixtures/extensions/test/test.test.mts new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/extensions/test/test.test.ts b/fixtures/extensions/test/test.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/helpers/find-file.ts b/src/helpers/find-file.ts index f86b817b..b182a72a 100644 --- a/src/helpers/find-file.ts +++ b/src/helpers/find-file.ts @@ -1,6 +1,7 @@ -/* c8 ignore start */ +/* c8 ignore next */ // c8 bug const regex = /at\s(\/.+|file:.+)|^(\s+)at\smodule\scode\s\((\/.+|file:.+)\)/i; +/* c8 ignore next */ // c8 bug export const findFile = (error: Error) => { const stackLines = error.stack?.split('\n') || []; @@ -9,7 +10,7 @@ export const findFile = (error: Error) => { const basePath = 'poku/lib/'; for (const line of stackLines) { - if (!line.includes(basePath)) { + if (line.indexOf(basePath) === -1) { const match = line.match(regex); // Node and Deno @@ -28,4 +29,3 @@ export const findFile = (error: Error) => { return file; }; -/* c8 ignore stop */ diff --git a/src/helpers/force-array.ts b/src/helpers/force-array.ts deleted file mode 100644 index c5031f8c..00000000 --- a/src/helpers/force-array.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* c8 ignore start */ -export const forceArray = (input: T | T[]): T[] => { - if (Array.isArray(input)) { - return input; - } - return [input]; -}; -/* c8 ignore stop */ diff --git a/src/helpers/get-arg.ts b/src/helpers/get-arg.ts index 5bf78742..418b6f6c 100644 --- a/src/helpers/get-arg.ts +++ b/src/helpers/get-arg.ts @@ -1,4 +1,4 @@ -/* c8 ignore start */ +/* c8 ignore next */ // c8 bug import { argv } from 'node:process'; const [, , ...processArgs] = argv; @@ -17,9 +17,13 @@ const regexQuotes = /''|""/; * command --arg # undefined * ``` */ -export const getArg = (arg: string, prefix = '--'): string | undefined => { +export const getArg = ( + arg: string, + prefix = '--', + baseArgs = processArgs +): string | undefined => { const argPattern = `${prefix}${arg}=`; - const argValue = processArgs.find((a) => a.startsWith(argPattern)); + const argValue = baseArgs.find((a) => a.startsWith(argPattern)); if (!argValue) { return undefined; @@ -40,10 +44,14 @@ export const getArg = (arg: string, prefix = '--'): string | undefined => { * command # false * ``` */ -export const hasArg = (arg: string, prefix = '--'): boolean => { +export const hasArg = ( + arg: string, + prefix = '--', + baseArgs = processArgs +): boolean => { const argPattern = `${prefix}${arg}`; - return processArgs.some((a) => a.startsWith(argPattern)); + return baseArgs.some((a) => a.startsWith(argPattern)); }; /** @@ -58,8 +66,11 @@ export const hasArg = (arg: string, prefix = '--'): boolean => { * command --arg # undefined * ``` */ -export const getLastParam = (prefix = '--'): string | undefined => { - const lastArg = processArgs[processArgs.length - 1]; +export const getLastParam = ( + prefix = '--', + baseArgs = processArgs +): string | undefined => { + const lastArg = baseArgs[baseArgs.length - 1]; if (!lastArg || lastArg.startsWith(prefix)) { return undefined; @@ -68,25 +79,31 @@ export const getLastParam = (prefix = '--'): string | undefined => { return lastArg; }; -export const argToArray = (arg: string, prefix = '--') => { - const hasArgument = hasArg(arg); +/* c8 ignore next */ // c8 bug +export const argToArray = ( + arg: string, + prefix = '--', + baseArgs = processArgs +) => { + const hasArgument = hasArg(arg, prefix, baseArgs); if (!hasArgument) { return undefined; } - const argValue = getArg(arg, prefix); + const argValue = getArg(arg, prefix, baseArgs); if (hasArgument && !argValue) { return []; } + /* c8 ignore start */ // Type safe if (!argValue) { return undefined; } + /* c8 ignore stop */ return argValue .split(',') .map((a) => a.trim()) .filter((a) => a); }; -/* c8 ignore stop */ diff --git a/src/helpers/get-runtime.ts b/src/helpers/get-runtime.ts index 7cb70401..ebb978b5 100644 --- a/src/helpers/get-runtime.ts +++ b/src/helpers/get-runtime.ts @@ -1,10 +1,12 @@ /* c8 ignore start */ -import { version } from 'node:process'; import type { Configs } from '../@types/poku.js'; +import { version } from 'node:process'; declare const Deno: unknown; declare const Bun: unknown; +const regex = /v(\d+)\./; + export const supportedPlatforms: readonly Configs['platform'][] = [ 'node', 'bun', @@ -33,17 +35,22 @@ export const getRuntime = ( return configs.platform; } + /* c8 ignore start */ if (typeof Deno !== 'undefined') { return 'deno'; } + /* c8 ignore stop */ + /* c8 ignore start */ if (typeof Bun !== 'undefined') { return 'bun'; } + /* c8 ignore stop */ return 'node'; }; +/* c8 ignore start */ export const nodeVersion = - getRuntime() === 'node' ? Number(version.match(/v(\d+)\./)?.[1]) : undefined; + getRuntime() === 'node' ? Number(version.match(regex)?.[1]) : undefined; /* c8 ignore stop */ diff --git a/src/helpers/logs.ts b/src/helpers/logs.ts index ec8fe33d..e83f1593 100644 --- a/src/helpers/logs.ts +++ b/src/helpers/logs.ts @@ -1,7 +1,8 @@ -/* c8 ignore start */ -import { stdout } from 'node:process'; +/* c8 ignore next */ import type { Configs } from '../@types/poku.js'; +/* c8 ignore next */ import type { Formatter } from './format.js'; +import { stdout } from 'node:process'; const regex = { newLine: /\n/, @@ -16,6 +17,7 @@ export const isDebug = (configs?: Configs): boolean => Boolean(configs?.debug); export const write = (data: string | Uint8Array | Formatter) => stdout.write(`${String(data)}\n`); +/* c8 ignore next */ // c8 bug export const printOutput = (options: { output: string; result: boolean; @@ -31,7 +33,7 @@ export const printOutput = (options: { debug || !result ? splittedOutput : splittedOutput.filter((current) => { - if (current.includes('Exited with code')) { + if (current.indexOf('Exited with code') !== -1) { return false; } return regex.ansi.test(JSON.stringify(current)) || current === ''; @@ -46,4 +48,3 @@ export const printOutput = (options: { write(mappedOutputs.join('\n')); }; -/* c8 ignore stop */ diff --git a/src/helpers/runner.ts b/src/helpers/runner.ts index 86812d23..1c346592 100644 --- a/src/helpers/runner.ts +++ b/src/helpers/runner.ts @@ -1,9 +1,9 @@ -/* c8 ignore start */ +/* c8 ignore start */ // Multi-platform +import type { Configs } from '../@types/poku.js'; +import type { Runner } from '../@types/runner.js'; import { platform } from 'node:process'; import { extname } from 'node:path'; import { getRuntime } from './get-runtime.js'; -import type { Configs } from '../@types/poku.js'; -import type { Runner } from '../@types/runner.js'; export const isWindows = platform === 'win32'; @@ -40,7 +40,7 @@ export const runner = (filename: string, configs?: Configs): string[] => { } // Node.js - return extname(filename) === '.ts' + return ['.ts', '.mts', '.cts'].includes(extname(filename)) ? [isWindows ? 'npx.cmd' : 'npx', 'tsx'] : ['node']; }; diff --git a/src/helpers/time.ts b/src/helpers/time.ts index edfeb794..81fbe2ee 100644 --- a/src/helpers/time.ts +++ b/src/helpers/time.ts @@ -1,4 +1,4 @@ -/* c8 ignore start */ +/* c8 ignore next */ // c8 bug import { padStart } from '../polyfills/pad.js'; export const setTime = (date: Date): string => { @@ -9,10 +9,10 @@ export const setTime = (date: Date): string => { return `${hours}:${minutes}:${seconds}`; }; +/* c8 ignore next */ // c8 bug export const toSecs = (milliseconds: string): string => { const ms = Number.parseFloat(milliseconds); const seconds = (ms / 1000).toFixed(2); return seconds; }; -/* c8 ignore stop */ diff --git a/src/modules/exit.ts b/src/modules/exit.ts index af6320e4..f701011e 100644 --- a/src/modules/exit.ts +++ b/src/modules/exit.ts @@ -1,9 +1,9 @@ -/* c8 ignore start */ +/* c8 ignore start */ // This module is entirely process-based +import type { Code } from '../@types/code.js'; import process from 'node:process'; import { hr } from '../helpers/hr.js'; import { results } from '../configs/poku.js'; import { format } from '../helpers/format.js'; -import type { Code } from '../@types/code.js'; import { write } from '../helpers/logs.js'; import { fileResults, finalResults } from '../configs/files.js'; import { setTime, toSecs } from '../helpers/time.js'; diff --git a/src/modules/list-files-sync.ts b/src/modules/list-files-sync.ts index 275e88a5..bf5bbf22 100644 --- a/src/modules/list-files-sync.ts +++ b/src/modules/list-files-sync.ts @@ -1,8 +1,4 @@ -/* c8 ignore start */ -/** - * This method will be removed in a future release to use `list-files.ts` instead. - */ - +/* c8 ignore start */ // This method will be removed in a future release to use `list-files.ts` instead import { env } from 'node:process'; import { readdirSync, statSync } from 'node:fs'; import path from 'node:path'; diff --git a/src/services/map-tests.ts b/src/services/map-tests.ts index b0be4b23..2f49cae8 100644 --- a/src/services/map-tests.ts +++ b/src/services/map-tests.ts @@ -53,7 +53,7 @@ export const findMatchingFiles = ( for (const fileWithExt of srcFilesWithExt) { const normalizedFileWithExt = normalizePath(fileWithExt); - if (normalizedFileWithExt.includes(normalizedSrcFile)) { + if (normalizedFileWithExt.indexOf(normalizedSrcFile) !== -1) { matchingFiles.add(fileWithExt); } } @@ -141,8 +141,8 @@ const createImportMap = async ( /* c8 ignore start */ if ( - content.includes(relativePath.replace(regex.extFilter, '')) || - content.includes(normalizedSrcFile) + content.indexOf(relativePath.replace(regex.extFilter, '')) !== -1 || + content.indexOf(normalizedSrcFile) !== -1 ) { if (!importMap.has(normalizedSrcFile)) { importMap.set(normalizedSrcFile, new Set()); diff --git a/src/services/pid.ts b/src/services/pid.ts index 3878ac95..237f6d97 100644 --- a/src/services/pid.ts +++ b/src/services/pid.ts @@ -1,15 +1,14 @@ -/* c8 ignore start */ +/* c8 ignore start */ // This module is entirely process-based import { spawn } from 'node:child_process'; -import { forceArray } from '../helpers/force-array.js'; const regex = { sequentialSpaces: /\s+/, } as const; export const setPortsAndPIDs = (portOrPID: number | number[]) => - forceArray(portOrPID) - .map((p) => Number(p)) - .filter((p) => !Number.isNaN(p)); + Array.isArray(portOrPID) + ? portOrPID + : [portOrPID].map((p) => Number(p)).filter((p) => !Number.isNaN(p)); export const populateRange = (startsAt: number, endsAt: number) => { const first = Number(startsAt); diff --git a/src/services/watch.ts b/src/services/watch.ts index 5f850dea..b1ee4d03 100644 --- a/src/services/watch.ts +++ b/src/services/watch.ts @@ -53,10 +53,11 @@ class Watcher { } private async watchDirectory(dir: string) { - /* c8 ignore next */ + /* c8 ignore start */ if (this.dirWatchers.has(dir)) { return; } + /* c8 ignore stop */ const watcher = nodeWatch(dir, async (_, filename) => { if (filename) { diff --git a/test/e2e/runners.test.ts b/test/e2e/runners.test.ts new file mode 100644 index 00000000..c359873f --- /dev/null +++ b/test/e2e/runners.test.ts @@ -0,0 +1,90 @@ +import process from 'node:process'; +import { execSync } from 'node:child_process'; +import { describe } from '../../src/modules/describe.js'; +import { it } from '../../src/modules/it.js'; +import { assert } from '../../src/modules/assert.js'; +import { isProduction, inspectCLI } from '../helpers/capture-cli.test.js'; + +if (isProduction) { + process.exit(0); +} + +const hasNode = (() => { + try { + execSync('node -v', { stdio: 'ignore' }); + return true; + } catch { + return false; + } +})(); + +const hasDeno = (() => { + try { + execSync('deno -v', { stdio: 'ignore' }); + return true; + } catch { + return false; + } +})(); + +const hasBun = (() => { + try { + execSync('bun -v', { stdio: 'ignore' }); + return true; + } catch { + return false; + } +})(); + +describe('Test Runtimes/Platforms + Extensions', async () => { + hasNode && + (await it('Node.js', async () => { + const output = await inspectCLI( + 'npx tsx src/bin/index.ts --platform=node fixtures/extensions' + ); + + assert.strictEqual(output.exitCode, 0, 'Exit Code needs to be 0'); + assert(/PASS › 12/.test(output.stdout), 'CLI needs to pass 1'); + assert(/FAIL › 0/.test(output.stdout), 'CLI needs to fail 0'); + assert(/node.+?\.js/.test(output.stdout), '.js => node'); + assert(/node.+?\.cjs/.test(output.stdout), '.cjs => node'); + assert(/node.+?\.mjs/.test(output.stdout), '.mjs => node'); + assert(/tsx.+?\.ts/.test(output.stdout), '.ts => tsx'); + assert(/tsx.+?\.cts/.test(output.stdout), '.cts => tsx'); + assert(/tsx.+?\.mts/.test(output.stdout), '.mts => tsx'); + })); + + hasBun && + (await it('Bun', async () => { + const output = await inspectCLI( + 'bun src/bin/index.ts --platform=bun fixtures/extensions' + ); + + assert.strictEqual(output.exitCode, 0, 'Exit Code needs to be 0'); + assert(/PASS › 12/.test(output.stdout), 'CLI needs to pass 1'); + assert(/FAIL › 0/.test(output.stdout), 'CLI needs to fail 0'); + assert(/bun.+?.js/.test(output.stdout), 'bun => .js'); + assert(/bun.+?.cjs/.test(output.stdout), 'bun => .cjs'); + assert(/bun.+?.mjs/.test(output.stdout), 'bun => .mjs'); + assert(/bun.+?.ts/.test(output.stdout), 'bun => .ts'); + assert(/bun.+?.cts/.test(output.stdout), 'bun => .cts'); + assert(/bun.+?.mts/.test(output.stdout), 'bun => .mts'); + })); + + hasDeno && + (await it('Deno', async () => { + const output = await inspectCLI( + 'deno run --unstable-sloppy-imports --allow-read --allow-env --allow-run src/bin/index.ts --platform=deno fixtures/extensions' + ); + + assert.strictEqual(output.exitCode, 0, 'Exit Code needs to be 0'); + assert(/PASS › 12/.test(output.stdout), 'CLI needs to pass 1'); + assert(/FAIL › 0/.test(output.stdout), 'CLI needs to fail 0'); + assert(/deno run.+?.js/.test(output.stdout), 'deno => .js'); + assert(/deno run.+?.cjs/.test(output.stdout), 'deno => .cjs'); + assert(/deno run.+?.mjs/.test(output.stdout), 'deno => .mjs'); + assert(/deno run.+?.ts/.test(output.stdout), 'deno => .ts'); + assert(/deno run.+?.cts/.test(output.stdout), 'deno => .cts'); + assert(/deno run.+?.mts/.test(output.stdout), 'deno => .mts'); + })); +}); diff --git a/test/helpers/capture-cli.test.ts b/test/helpers/capture-cli.test.ts index 05e22340..fd78af90 100644 --- a/test/helpers/capture-cli.test.ts +++ b/test/helpers/capture-cli.test.ts @@ -1,5 +1,5 @@ import process from 'node:process'; -import { spawn } from 'node:child_process'; +import { spawn, type SpawnOptionsWithoutStdio } from 'node:child_process'; import { isWindows, runner } from '../../src/helpers/runner.js'; // `/_.ts`: Simulate TypeScript file for Deno @@ -42,3 +42,35 @@ export const executeCLI = (args: string[]): Promise => } }); }); + +export const inspectCLI = ( + command: string, + options?: SpawnOptionsWithoutStdio +): Promise<{ stdout: string; stderr: string; exitCode: number }> => + new Promise((resolve, reject) => { + const [cmd, ...args] = command.split(' '); + + const childProcess = spawn(cmd, args, { + shell: isWindows, + ...options, + }); + + let stdout = ''; + let stderr = ''; + + childProcess.stdout.on('data', (data: Buffer) => { + stdout += data.toString(); + }); + + childProcess.stderr.on('data', (data: Buffer) => { + stderr += data.toString(); + }); + + childProcess.on('error', (error: Error) => { + reject({ error: error.message, stdout, stderr, exitCode: 1 }); + }); + + childProcess.on('close', (code: number) => { + resolve({ stdout, stderr, exitCode: code }); + }); + }); diff --git a/test/integration/describe/describe.test.ts b/test/integration/describe/describe.test.ts index f8611119..645de385 100644 --- a/test/integration/describe/describe.test.ts +++ b/test/integration/describe/describe.test.ts @@ -1,5 +1,6 @@ import { describe } from '../../../src/modules/describe.js'; import { test } from '../../../src/modules/test.js'; +import { log } from '../../../src/modules/log.js'; describe('Testing "describe" method', { icon: '🔬', @@ -39,3 +40,7 @@ test('Using as groups', async () => { await describe('', async () => await new Promise((resolve) => resolve(undefined))); }); + +test('Using log "mini" helper', () => { + log('Custom Log'); +}); diff --git a/test/unit/args.test.ts b/test/unit/args.test.ts new file mode 100644 index 00000000..1e2c5993 --- /dev/null +++ b/test/unit/args.test.ts @@ -0,0 +1,73 @@ +import { describe } from '../../src/modules/describe.js'; +import { it } from '../../src/modules/it.js'; +import { assert } from '../../src/modules/assert.js'; +import { + getArg, + hasArg, + getLastParam, + argToArray, +} from '../../src/helpers/get-arg.js'; + +describe('CLI Argument Handling Functions', async () => { + await it('should get argument value', () => { + const args = ['--arg=value']; + const result = getArg('arg', '--', args); + assert.strictEqual(result, 'value', 'Argument value should be "value"'); + }); + + await it('should return undefined for missing argument value', () => { + const args = ['--arg']; + const result = getArg('arg', '--', args); + assert.strictEqual(result, undefined, 'Argument value should be undefined'); + }); + + await it('should check if argument exists', () => { + const args = ['--checkArg']; + const result = hasArg('checkArg', '--', args); + assert.strictEqual(result, true, 'Argument should exist'); + }); + + await it('should check if argument does not exist', () => { + const args = ['--anotherArg']; + const result = hasArg('checkArg', '--', args); + assert.strictEqual(result, false, 'Argument should not exist'); + }); + + await it('should get the last parameter', () => { + const args = ['value']; + const result = getLastParam('--', args); + assert.strictEqual(result, 'value', 'Last parameter should be "value"'); + }); + + await it('should return undefined for last parameter if none provided', () => { + const args: string[] = []; + const result = getLastParam('--', args); + assert.strictEqual(result, undefined, 'Last parameter should be undefined'); + }); + + await it('should convert argument to array', () => { + const args = ['--array=1,2,3']; + const result = argToArray('array', '--', args); + assert.deepStrictEqual( + result, + ['1', '2', '3'], + 'Argument should be converted to array [1, 2, 3]' + ); + }); + + await it('should return empty array for argument without value', () => { + const args = ['--array']; + const result = argToArray('array', '--', args); + assert.deepStrictEqual( + result, + [], + 'Argument should be converted to an empty array' + ); + }); + + await it('should return undefined for non-existing argument to array', () => { + const args: string[] = []; + const result = argToArray('array', '--', args); + assert.strictEqual(result, undefined, 'Argument should be undefined'); + }); +}); diff --git a/test/unit/assert-find-file.test.ts b/test/unit/assert-find-file.test.ts index 45a46704..6e178bd8 100644 --- a/test/unit/assert-find-file.test.ts +++ b/test/unit/assert-find-file.test.ts @@ -2,7 +2,7 @@ import { test } from '../../src/modules/test.js'; import { assert } from '../../src/modules/assert.js'; import { findFile } from '../../src/helpers/find-file.js'; -const setStack = (stack: string): Error => { +const setStack = (stack?: string): Error => { const error = new Error('Test error'); error.stack = stack; @@ -66,6 +66,29 @@ const testCases = [ `, expected: '', }, + { + description: 'Bun (legacy)', + stack: ` + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + 123 !== 456 + at message.message (/workdir/node_modules/poku/lib/modules/assert.js:53:63) + at /workdir/node_modules/poku/lib/helpers/parse-assertion.js:79:26 + at Generator.next () + at /workdir/node_modules/poku/lib/helpers/parse-assertion.js:8:71 + at new Promise () + at __awaiter (/workdir/node_modules/poku/lib/helpers/parse-assertion.js:4:12) + at parseAssertion (/workdir/node_modules/poku/lib/helpers/parse-assertion.js:62:41) + at Function.strictEqual (/workdir/node_modules/poku/lib/modules/assert.js:53:45) + at module code (/workdir/test/some.test.js:3:8) + at ModuleJob.run (node:internal/modules/esm/module_job:262:25) + `, + expected: '/workdir/test/some.test.js:3:8', + }, + { + description: 'Invalid Stack', + stack: undefined, + expected: '', + }, ]; test('Assert: Find file from stack', () => { diff --git a/test/unit/logs.test.ts b/test/unit/logs.test.ts new file mode 100644 index 00000000..06114e43 --- /dev/null +++ b/test/unit/logs.test.ts @@ -0,0 +1,141 @@ +import process from 'node:process'; +import { describe } from '../../src/modules/describe.js'; +import { it } from '../../src/modules/it.js'; +import { assert } from '../../src/modules/assert.js'; +import { + isQuiet, + isDebug, + write, + printOutput, +} from '../../src/helpers/logs.js'; + +describe('Helper functions in logs.js', () => { + it('should return true if configs.quiet is true', () => { + const configs = { quiet: true }; + const result = isQuiet(configs); + assert.strictEqual(result, true, 'isQuiet should return true'); + }); + + it('should return false if configs.quiet is false', () => { + const configs = { quiet: false }; + const result = isQuiet(configs); + assert.strictEqual(result, false, 'isQuiet should return false'); + }); + + it('should return false if configs.quiet is undefined', () => { + const configs = {}; + const result = isQuiet(configs); + assert.strictEqual(result, false, 'isQuiet should return false'); + }); +}); + +describe('Helper functions in logs.js', () => { + it('should return true if configs.debug is true', () => { + const configs = { debug: true }; + const result = isDebug(configs); + assert.strictEqual(result, true, 'isDebug should return true'); + }); + + it('should return false if configs.debug is false', () => { + const configs = { debug: false }; + const result = isDebug(configs); + assert.strictEqual(result, false, 'isDebug should return false'); + }); + + it('should return false if configs.debug is undefined', () => { + const configs = {}; + const result = isDebug(configs); + assert.strictEqual(result, false, 'isDebug should return false'); + }); +}); + +describe('Helper functions in logs.js', () => { + it('should write data to stdout', () => { + let capturedOutput = ''; + const originalWrite = process.stdout.write; + process.stdout.write = (data: string) => { + capturedOutput += data; + return true; + }; + + write('Test message'); + assert.strictEqual( + capturedOutput, + 'Test message\n', + 'write should output the message to stdout with a newline' + ); + + process.stdout.write = originalWrite; + }); +}); + +describe('Helper functions in logs.js', () => { + it('should print output correctly with debug on', () => { + let capturedOutput = ''; + const originalWrite = process.stdout.write; + process.stdout.write = (data: string) => { + capturedOutput += data; + return true; + }; + + const options = { + output: 'Line1\nLine2\nLine3', + result: true, + configs: { debug: true }, + }; + printOutput(options); + assert.strictEqual( + capturedOutput, + ' Line1\n Line2\n Line3\n', + 'printOutput should print all lines with padding' + ); + + process.stdout.write = originalWrite; + }); + + it('should filter and print output correctly with debug off', () => { + let capturedOutput = ''; + const originalWrite = process.stdout.write; + process.stdout.write = (data: string) => { + capturedOutput += data; + return true; + }; + + const options = { + output: 'Line1\n\x1b[0mLine2\x1b[0m\nExited with code', + result: true, + configs: { debug: false }, + }; + printOutput(options); + assert.strictEqual( + capturedOutput, + ' \x1b[0mLine2\x1b[0m\n', + 'printOutput should filter lines correctly and print with padding' + ); + + process.stdout.write = originalWrite; + }); + + it('should return early if outputs length is 0', () => { + let capturedOutput = ''; + const originalWrite = process.stdout.write; + process.stdout.write = (data: string) => { + capturedOutput += data; + return true; + }; + + const options = { + output: '', + result: true, + configs: { debug: false }, + }; + printOutput(options); + assert.strictEqual( + capturedOutput, + '', + 'printOutput should not print anything if outputs length is 0' + ); + + process.stdout.write = originalWrite; + }); +});