From b1feeac327c306bb7febefd422e688de05b963cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Weslley=20Ara=C3=BAjo?= <46850407+wellwelwel@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:17:53 -0300 Subject: [PATCH] feat: introduce custom reporters (`poku`, `dot`, `compact`, `focus`, `verbose`, and `classic`) (#921) * feat: initialize custom reporters * chore: adjust runtime for Windows * chore: adjust runtime for WSL * chore: remove debug * refactor: improve runtime identification * chore: check for Deno version * refactor: use global runtime * ci: enable CI for `reporter` branch * ci: remove canary from matrix * debug: disable coverage check * ci: check lint for all push events * refactor: improve plugin param names * chore: adjust params * chore: add file events * refactor: resume logic * ci: improve c8 comments * debug: restore canary versions from matrix * chore: create reporters * ci: revert c8 comments * ci: add e2e tests * chore: improve coverage * ci: add test to `--listFiles` command * ci: remove debug * chore: improve verbose reporter * chore: support for config files * refactor: use number for durations * refactor: use number for durations * perf: remove file map from global memory * ci: improve CI timeout * chore(website): remove deprecated Sass usage * docs: add `reporter` documentation --- .github/workflows/bot_deps-docs-update.yml | 1 + .github/workflows/bot_deps-update.yml | 1 + .github/workflows/bot_stale.yml | 1 + .github/workflows/ci_codeql.yml | 2 +- .github/workflows/ci_coverage-linux.yml | 2 +- .github/workflows/ci_coverage-osx.yml | 2 +- .github/workflows/ci_lint.yml | 2 - .nycrc/darwin.jsonc | 4 +- .nycrc/linux.jsonc | 4 +- .nycrc/win32.jsonc | 4 +- biome.jsonc | 2 +- package.json | 1 - src/@types/list-files.ts | 5 - src/@types/poku.ts | 64 +++- src/bin/help.ts | 6 +- src/bin/index.ts | 21 +- src/bin/watch.ts | 14 +- src/builders/reporter.ts | 9 + src/configs/files.ts | 11 - src/configs/poku.ts | 24 +- src/globals.d.ts | 9 + src/modules/essentials/poku.ts | 31 +- src/modules/essentials/strict.ts | 5 +- src/modules/helpers/create-service.ts | 3 +- src/modules/helpers/describe.ts | 35 +-- src/modules/helpers/exit.ts | 60 +--- src/modules/helpers/get-pids.ts | 2 +- src/modules/helpers/it/core.ts | 42 +-- src/modules/helpers/kill.ts | 2 +- src/modules/helpers/list-files.ts | 2 +- src/modules/helpers/modifiers.ts | 13 +- src/modules/helpers/skip.ts | 15 +- src/parsers/get-runner.ts | 15 +- src/parsers/get-runtime.ts | 24 +- src/parsers/options.ts | 2 +- src/parsers/os.ts | 3 + src/parsers/output.ts | 2 +- src/parsers/runtime-version.ts | 8 + src/parsers/time.ts | 7 +- src/services/assert.ts | 91 +----- src/services/container.ts | 7 +- src/services/each.ts | 8 +- src/services/format.ts | 34 +-- src/services/reporter.ts | 16 + src/services/reporters/classic.ts | 62 ++++ src/services/reporters/compact.ts | 51 ++++ src/services/reporters/dot.ts | 33 +++ src/services/reporters/focus.ts | 42 +++ src/services/reporters/poku.ts | 244 ++++++++++++++++ src/services/reporters/verbose.ts | 18 ++ src/services/run-test-file.ts | 76 +++-- src/services/run-tests.ts | 17 +- test/__fixtures__/e2e/each-file/api.test.ts | 226 +++++++++++++++ test/__fixtures__/e2e/env/set-env.test.ts | 17 +- test/__fixtures__/e2e/list-files/a.cjs | 0 test/__fixtures__/e2e/list-files/a.cts | 0 test/__fixtures__/e2e/list-files/a.js | 0 test/__fixtures__/e2e/list-files/a.mjs | 0 test/__fixtures__/e2e/list-files/a.mts | 0 test/__fixtures__/e2e/list-files/a.spec.cjs | 0 test/__fixtures__/e2e/list-files/a.spec.cts | 0 test/__fixtures__/e2e/list-files/a.spec.js | 0 test/__fixtures__/e2e/list-files/a.spec.mjs | 0 test/__fixtures__/e2e/list-files/a.spec.mts | 0 test/__fixtures__/e2e/list-files/a.spec.ts | 0 test/__fixtures__/e2e/list-files/a.test.cjs | 0 test/__fixtures__/e2e/list-files/a.test.cts | 0 test/__fixtures__/e2e/list-files/a.test.js | 0 test/__fixtures__/e2e/list-files/a.test.mjs | 0 test/__fixtures__/e2e/list-files/a.test.mts | 0 test/__fixtures__/e2e/list-files/a.test.ts | 0 test/__fixtures__/e2e/list-files/a.ts | 0 .../__fixtures__/e2e/list-files/depth-1/a.cjs | 0 .../__fixtures__/e2e/list-files/depth-1/a.cts | 0 test/__fixtures__/e2e/list-files/depth-1/a.js | 0 .../__fixtures__/e2e/list-files/depth-1/a.mjs | 0 .../__fixtures__/e2e/list-files/depth-1/a.mts | 0 .../e2e/list-files/depth-1/a.spec.cjs | 0 .../e2e/list-files/depth-1/a.spec.cts | 0 .../e2e/list-files/depth-1/a.spec.js | 0 .../e2e/list-files/depth-1/a.spec.mjs | 0 .../e2e/list-files/depth-1/a.spec.mts | 0 .../e2e/list-files/depth-1/a.spec.ts | 0 .../e2e/list-files/depth-1/a.test.cjs | 0 .../e2e/list-files/depth-1/a.test.cts | 0 .../e2e/list-files/depth-1/a.test.js | 0 .../e2e/list-files/depth-1/a.test.mjs | 0 .../e2e/list-files/depth-1/a.test.mts | 0 .../e2e/list-files/depth-1/a.test.ts | 0 test/__fixtures__/e2e/list-files/depth-1/a.ts | 0 .../e2e/list-files/depth-1/depth-2/a.cjs | 0 .../e2e/list-files/depth-1/depth-2/a.cts | 0 .../e2e/list-files/depth-1/depth-2/a.js | 0 .../e2e/list-files/depth-1/depth-2/a.mjs | 0 .../e2e/list-files/depth-1/depth-2/a.mts | 0 .../e2e/list-files/depth-1/depth-2/a.spec.cjs | 0 .../e2e/list-files/depth-1/depth-2/a.spec.cts | 0 .../e2e/list-files/depth-1/depth-2/a.spec.js | 0 .../e2e/list-files/depth-1/depth-2/a.spec.mjs | 0 .../e2e/list-files/depth-1/depth-2/a.spec.mts | 0 .../e2e/list-files/depth-1/depth-2/a.spec.ts | 0 .../e2e/list-files/depth-1/depth-2/a.test.cjs | 0 .../e2e/list-files/depth-1/depth-2/a.test.cts | 0 .../e2e/list-files/depth-1/depth-2/a.test.js | 0 .../e2e/list-files/depth-1/depth-2/a.test.mjs | 0 .../e2e/list-files/depth-1/depth-2/a.test.mts | 0 .../e2e/list-files/depth-1/depth-2/a.test.ts | 0 .../e2e/list-files/depth-1/depth-2/a.ts | 0 .../list-files/depth-1/depth-2/depth-3/a.cjs | 0 .../list-files/depth-1/depth-2/depth-3/a.cts | 0 .../list-files/depth-1/depth-2/depth-3/a.js | 0 .../list-files/depth-1/depth-2/depth-3/a.mjs | 0 .../list-files/depth-1/depth-2/depth-3/a.mts | 0 .../depth-1/depth-2/depth-3/a.spec.cjs | 0 .../depth-1/depth-2/depth-3/a.spec.cts | 0 .../depth-1/depth-2/depth-3/a.spec.js | 0 .../depth-1/depth-2/depth-3/a.spec.mjs | 0 .../depth-1/depth-2/depth-3/a.spec.mts | 0 .../depth-1/depth-2/depth-3/a.spec.ts | 0 .../depth-1/depth-2/depth-3/a.test.cjs | 0 .../depth-1/depth-2/depth-3/a.test.cts | 0 .../depth-1/depth-2/depth-3/a.test.js | 0 .../depth-1/depth-2/depth-3/a.test.mjs | 0 .../depth-1/depth-2/depth-3/a.test.mts | 0 .../depth-1/depth-2/depth-3/a.test.ts | 0 .../list-files/depth-1/depth-2/depth-3/a.ts | 0 .../e2e/reporters/failure/a.test.ts | 9 + .../e2e/reporters/failure/b.test.ts | 3 + .../e2e/reporters/failure/c.test.ts | 3 + .../e2e/reporters/failure/d.test.ts | 3 + .../e2e/reporters/hybrid/a.test.ts | 3 + .../e2e/reporters/hybrid/b.test.ts | 3 + .../e2e/reporters/hybrid/c.test.ts | 3 + .../e2e/reporters/hybrid/d.test.ts | 3 + .../e2e/reporters/hybrid/e.test.ts | 3 + .../e2e/reporters/hybrid/f.test.ts | 8 + .../e2e/reporters/success/a.test.ts | 9 + .../e2e/reporters/success/b.test.ts | 3 + .../e2e/reporters/success/c.test.ts | 3 + .../e2e/reporters/success/d.test.ts | 3 + test/__utils__/capture-cli.test.ts | 7 +- test/c8.test.ts | 2 +- test/e2e/background-process.test.ts | 4 +- test/e2e/before-and-after-each.test.ts | 263 +++-------------- test/e2e/cli-ensure-flags.test.ts | 8 +- test/e2e/cli-flags.test.ts | 4 +- test/e2e/config-files.test.ts | 12 +- test/e2e/each-api-order.test.ts | 4 +- test/e2e/env-file.test.ts | 6 +- test/e2e/fail-fast.test.ts | 4 +- test/e2e/failure.test.ts | 8 +- test/e2e/help.test.ts | 6 +- test/e2e/ignored-paths.test.ts | 2 +- test/e2e/list-files.test.ts | 163 +++++++++++ ...o-tests-with-unlimited-concurrency.test.ts | 4 +- test/e2e/only.test.ts | 45 ++- test/e2e/reporters.test.ts | 135 +++++++++ test/e2e/runners.test.ts | 10 +- test/e2e/sequential.test.ts | 4 +- test/e2e/watch.test.ts | 12 +- .../external-file-update.test.ts | 5 +- .../before-and-after-each/invalid.test.ts | 4 +- .../containers/test-docker-compose.test.ts | 2 +- .../containers/test-dockerfile.test.ts | 2 +- test/integration/import.test.ts | 4 +- .../strict/assert-no-message.test.ts | 16 +- test/integration/strict/assert.test.ts | 7 +- test/integration/strict/ensure-strict.test.ts | 8 +- test/unit/define-configs.test.ts | 4 +- test/unit/deno/allow.test.ts | 3 +- test/unit/deno/cjs.test.ts | 8 +- test/unit/deno/deny.test.ts | 7 +- .../wait-for/wait-for-expected-result.test.ts | 4 +- test/unit/watch.test.ts | 17 +- .../documentation/poku/options/reporter.mdx | 274 ++++++++++++++++++ .../documentation/poku/options/sequential.mdx | 2 +- website/schemas/options.json | 14 +- website/src/css/_history.scss | 2 + website/src/css/_personal.scss | 2 + website/src/css/custom.scss | 14 +- website/src/css/features.scss | 2 +- website/src/css/home.scss | 2 +- website/src/css/stability/_main.scss | 5 +- 183 files changed, 1820 insertions(+), 752 deletions(-) create mode 100644 src/builders/reporter.ts delete mode 100644 src/configs/files.ts create mode 100644 src/globals.d.ts create mode 100644 src/parsers/os.ts create mode 100644 src/parsers/runtime-version.ts create mode 100644 src/services/reporter.ts create mode 100644 src/services/reporters/classic.ts create mode 100644 src/services/reporters/compact.ts create mode 100644 src/services/reporters/dot.ts create mode 100644 src/services/reporters/focus.ts create mode 100644 src/services/reporters/poku.ts create mode 100644 src/services/reporters/verbose.ts create mode 100644 test/__fixtures__/e2e/each-file/api.test.ts create mode 100644 test/__fixtures__/e2e/list-files/a.cjs create mode 100644 test/__fixtures__/e2e/list-files/a.cts create mode 100644 test/__fixtures__/e2e/list-files/a.js create mode 100644 test/__fixtures__/e2e/list-files/a.mjs create mode 100644 test/__fixtures__/e2e/list-files/a.mts create mode 100644 test/__fixtures__/e2e/list-files/a.spec.cjs create mode 100644 test/__fixtures__/e2e/list-files/a.spec.cts create mode 100644 test/__fixtures__/e2e/list-files/a.spec.js create mode 100644 test/__fixtures__/e2e/list-files/a.spec.mjs create mode 100644 test/__fixtures__/e2e/list-files/a.spec.mts create mode 100644 test/__fixtures__/e2e/list-files/a.spec.ts create mode 100644 test/__fixtures__/e2e/list-files/a.test.cjs create mode 100644 test/__fixtures__/e2e/list-files/a.test.cts create mode 100644 test/__fixtures__/e2e/list-files/a.test.js create mode 100644 test/__fixtures__/e2e/list-files/a.test.mjs create mode 100644 test/__fixtures__/e2e/list-files/a.test.mts create mode 100644 test/__fixtures__/e2e/list-files/a.test.ts create mode 100644 test/__fixtures__/e2e/list-files/a.ts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.cjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.cts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.js create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.mjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.mts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.spec.cjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.spec.cts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.spec.js create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.spec.mjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.spec.mts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.spec.ts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.test.cjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.test.cts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.test.js create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.test.mjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.test.mts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.test.ts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/a.ts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.cjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.cts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.js create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.mjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.mts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.cjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.cts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.js create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.mjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.mts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.ts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.cjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.cts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.js create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.mjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.mts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.ts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/a.ts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.cjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.cts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.js create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.mjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.mts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.cjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.cts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.js create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.mjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.mts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.ts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.cjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.cts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.js create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.mjs create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.mts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.ts create mode 100644 test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.ts create mode 100644 test/__fixtures__/e2e/reporters/failure/a.test.ts create mode 100644 test/__fixtures__/e2e/reporters/failure/b.test.ts create mode 100644 test/__fixtures__/e2e/reporters/failure/c.test.ts create mode 100644 test/__fixtures__/e2e/reporters/failure/d.test.ts create mode 100644 test/__fixtures__/e2e/reporters/hybrid/a.test.ts create mode 100644 test/__fixtures__/e2e/reporters/hybrid/b.test.ts create mode 100644 test/__fixtures__/e2e/reporters/hybrid/c.test.ts create mode 100644 test/__fixtures__/e2e/reporters/hybrid/d.test.ts create mode 100644 test/__fixtures__/e2e/reporters/hybrid/e.test.ts create mode 100644 test/__fixtures__/e2e/reporters/hybrid/f.test.ts create mode 100644 test/__fixtures__/e2e/reporters/success/a.test.ts create mode 100644 test/__fixtures__/e2e/reporters/success/b.test.ts create mode 100644 test/__fixtures__/e2e/reporters/success/c.test.ts create mode 100644 test/__fixtures__/e2e/reporters/success/d.test.ts create mode 100644 test/e2e/list-files.test.ts create mode 100644 test/e2e/reporters.test.ts create mode 100644 website/docs/documentation/poku/options/reporter.mdx diff --git a/.github/workflows/bot_deps-docs-update.yml b/.github/workflows/bot_deps-docs-update.yml index 8e1f7993..c08934d4 100644 --- a/.github/workflows/bot_deps-docs-update.yml +++ b/.github/workflows/bot_deps-docs-update.yml @@ -18,6 +18,7 @@ env: jobs: update-deps: runs-on: ubuntu-latest + timeout-minutes: 10 if: github.event.repository.fork == false name: Update steps: diff --git a/.github/workflows/bot_deps-update.yml b/.github/workflows/bot_deps-update.yml index 521141f1..2fe86bf2 100644 --- a/.github/workflows/bot_deps-update.yml +++ b/.github/workflows/bot_deps-update.yml @@ -17,6 +17,7 @@ env: jobs: update-deps: runs-on: ubuntu-latest + timeout-minutes: 10 if: github.event.repository.fork == false name: Update steps: diff --git a/.github/workflows/bot_stale.yml b/.github/workflows/bot_stale.yml index 93eee503..2c161c71 100644 --- a/.github/workflows/bot_stale.yml +++ b/.github/workflows/bot_stale.yml @@ -8,6 +8,7 @@ on: jobs: stale: runs-on: ubuntu-latest + timeout-minutes: 5 name: Issues and PRs steps: - uses: actions/stale@v9 diff --git a/.github/workflows/ci_codeql.yml b/.github/workflows/ci_codeql.yml index 3d6b52cb..c44d71cd 100644 --- a/.github/workflows/ci_codeql.yml +++ b/.github/workflows/ci_codeql.yml @@ -27,7 +27,7 @@ jobs: # - https://gh.io/using-larger-runners # Consider using larger runners for possible analysis time improvements. runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} - timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + timeout-minutes: 15 permissions: actions: read contents: read diff --git a/.github/workflows/ci_coverage-linux.yml b/.github/workflows/ci_coverage-linux.yml index 0c6c52e0..fecd5325 100644 --- a/.github/workflows/ci_coverage-linux.yml +++ b/.github/workflows/ci_coverage-linux.yml @@ -10,7 +10,7 @@ on: jobs: linux: runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 strategy: fail-fast: false name: Linux diff --git a/.github/workflows/ci_coverage-osx.yml b/.github/workflows/ci_coverage-osx.yml index 85fd6d88..324f3050 100644 --- a/.github/workflows/ci_coverage-osx.yml +++ b/.github/workflows/ci_coverage-osx.yml @@ -10,7 +10,7 @@ on: jobs: osx: runs-on: macos-latest - timeout-minutes: 5 + timeout-minutes: 10 strategy: fail-fast: false name: macOS diff --git a/.github/workflows/ci_lint.yml b/.github/workflows/ci_lint.yml index 12db7008..0d415fd1 100644 --- a/.github/workflows/ci_lint.yml +++ b/.github/workflows/ci_lint.yml @@ -2,8 +2,6 @@ name: '👔 CI — Lint' on: push: - branches: - - 'main' pull_request: workflow_dispatch: diff --git a/.nycrc/darwin.jsonc b/.nycrc/darwin.jsonc index be62e431..9f7a1fff 100644 --- a/.nycrc/darwin.jsonc +++ b/.nycrc/darwin.jsonc @@ -7,6 +7,7 @@ { "exclude": [ "src/@types", // Typings exports only + "src/globals.d.ts", // Typings exports only "src/polyfills", // They involve compatibility between older versions of platforms or assist in cross-platform compatibility "src/modules/helpers/create-service.ts", // Varies of platform (Node.js, Bun, and Deno) "src/modules/helpers/container.ts", // On macOS, the memory consumption and installation time plus the processing required cause constant failures due to timeouts or resource limit usage (this test isn't skipped locally) @@ -14,8 +15,9 @@ "src/modules/helpers/kill.ts", // 100% Process-based "src/parsers/get-runner.ts", // Varies of platform (npm, yarn, bun, deno, pnpm, etc.) "src/parsers/get-runtime.ts", // Varies of platform (Node.js, Bun, and Deno) + "src/parsers/runtime-version.ts", // Varies of platform (Node.js, Bun, and Deno) "src/services/container.ts", // On macOS, the memory consumption and installation time plus the processing required cause constant failures due to timeouts or resource limit usage (this test isn't skipped locally) "src/services/pid.ts", // 100% Process-based - "src/bin" // TODO: Remove after expose `inpectCLI` and `watchCLI` methods + "src/bin/watch.ts" // Blocked by TSX ] } diff --git a/.nycrc/linux.jsonc b/.nycrc/linux.jsonc index 16923c39..9d9cf665 100644 --- a/.nycrc/linux.jsonc +++ b/.nycrc/linux.jsonc @@ -7,6 +7,7 @@ { "exclude": [ "src/@types", // Typings exports only + "src/globals.d.ts", // Typings exports only "src/polyfills", // They involve compatibility between older versions of platforms or assist in cross-platform compatibility "src/modules/helpers/container.ts", // Unexpectedly, GitHub Actions started refusing a simple connection (`node:http`), not directly related to the container test (this test isn't skipped locally) "src/modules/helpers/create-service.ts", // Varies of platform (Node.js, Bun, and Deno) @@ -14,8 +15,9 @@ "src/modules/helpers/kill.ts", // 100% Process-based "src/parsers/get-runner.ts", // Varies of platform (Node.js, Bun, and Deno) "src/parsers/get-runtime.ts", // Varies of platform (Node.js, Bun, and Deno) + "src/parsers/runtime-version.ts", // Varies of platform (Node.js, Bun, and Deno) "src/services/container.ts", // Unexpectedly, GitHub Actions started refusing a simple connection (`node:http`), not directly related to the container test (this test isn't skipped locally) "src/services/pid.ts", // 100% Process-based - "src/bin" // TODO: Remove after expose `inpectCLI` and `watchCLI` methods + "src/bin/watch.ts" // Blocked by TSX ] } diff --git a/.nycrc/win32.jsonc b/.nycrc/win32.jsonc index 55d30c1a..ef06a514 100644 --- a/.nycrc/win32.jsonc +++ b/.nycrc/win32.jsonc @@ -7,6 +7,7 @@ { "exclude": [ "src/@types", // Typings exports only + "src/globals.d.ts", // Typings exports only "src/polyfills", // They involve compatibility between older versions of platforms or assist in cross-platform compatibility "src/modules/helpers/create-service.ts", // Varies of platform (Node.js, Bun, and Deno) "src/modules/helpers/container.ts", // On Windows-based GitHub Actions, it's not possible to pull images based on manifest windows/amd64 @@ -14,8 +15,9 @@ "src/modules/helpers/kill.ts", // 100% Process-based "src/parsers/get-runner.ts", // Varies of platform (Node.js, Bun, and Deno) "src/parsers/get-runtime.ts", // Varies of platform (Node.js, Bun, and Deno) + "src/parsers/runtime-version.ts", // Varies of platform (Node.js, Bun, and Deno) "src/services/pid.ts", // 100% Process-based "src/services/container.ts", // On Windows-based GitHub Actions, it's not possible to pull images based on manifest windows/amd64 - "src/bin" // TODO: Remove after expose `inpectCLI` and `watchCLI` methods + "src/bin/watch.ts" // Blocked by TSX ] } diff --git a/biome.jsonc b/biome.jsonc index 8625fd1e..36c5d411 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -77,7 +77,7 @@ } }, "javascript": { - "globals": ["BufferEncoding"] + "globals": ["BufferEncoding", "Bun", "Deno"] }, "overrides": [ { diff --git a/package.json b/package.json index d65383b3..fa8c0034 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "description": "🐷 Poku makes testing easy for Node.js, Bun, Deno, and you at the same time.", "main": "./lib/modules/index.js", "license": "MIT", - "type": "commonjs", "bin": { "poku": "./lib/bin/index.js" }, diff --git a/src/@types/list-files.ts b/src/@types/list-files.ts index 33dc083b..8fae52d0 100644 --- a/src/@types/list-files.ts +++ b/src/@types/list-files.ts @@ -12,8 +12,3 @@ export type Configs = { */ exclude?: RegExp | RegExp[]; }; - -export type FileResults = { - success: Map; - fail: Map; -}; diff --git a/src/@types/poku.ts b/src/@types/poku.ts index 0fbe6624..bc7f0902 100644 --- a/src/@types/poku.ts +++ b/src/@types/poku.ts @@ -1,4 +1,10 @@ +import type { AssertionError } from 'node:assert'; +import type { results } from '../configs/poku.js'; import type { Configs as ListFilesConfigs } from './list-files.js'; +import type { ProcessAssertionOptions } from './assert.js'; +import type { DescribeOptions } from './describe.js'; + +type CustomString = string & NonNullable; export type DenoOptions = { allow?: string[]; @@ -8,6 +14,15 @@ export type DenoOptions = { export type Runtime = 'node' | 'bun' | 'deno'; +export type Reporter = + | 'poku' + | 'focus' + | 'dot' + | 'verbose' + | 'compact' + | 'classic' + | CustomString; + export type Configs = { /** * By setting `true`, **Poku** won't exit the process and will return the exit code (`0` or `1`). @@ -47,6 +62,10 @@ export type Configs = { * @default (availableParallelism() || cpus().lenght) - 1 */ concurrency?: number; + /** + * @default "poku" + */ + reporter?: Reporter; /** * You can use this option to run a **callback** or a **file** before each test file on your suite. * @@ -78,9 +97,11 @@ export type Configs = { deno?: DenoOptions; } & ListFilesConfigs; -export type FinalResults = { - time: string; +export type Timespan = { started: Date; + finished: Date; + /** Calculation from `process.hrtime()`. */ + duration: number; }; export type States = { @@ -113,3 +134,42 @@ export type ConfigJSONFile = { cliConfigs; export type ConfigFile = Omit & cliConfigs; + +type Results = { + code: number; + timespan: Timespan; + results: typeof results; +}; + +type Path = { + absolute: string; + relative: string; +}; + +export type ReporterPlugin = (configs?: Configs) => { + onRunStart: () => void; + onDescribeAsTitle: (title: string, options: DescribeOptions) => void; + onDescribeStart: (options: { title?: string }) => void; + onDescribeEnd: (options: { title?: string; duration: number }) => void; + onItStart: (options: { title?: string }) => void; + onItEnd: (options: { title?: string; duration: number }) => void; + onAssertionSuccess: (options: { message: string }) => void; + onAssertionFailure: (options: { + assertOptions: ProcessAssertionOptions; + error: AssertionError; + }) => void; + onSkipFile: (options: { message: string }) => void; + onSkipModifier: (options: { message: string }) => void; + onTodoModifier: (options: { message: string }) => void; + onFileStart: (options: { path: Path }) => void; + onFileResult: (options: { + status: boolean; + path: Path; + duration: number; + output?: string; + }) => void; + onRunResult: (options: Results) => void; + onExit: (options: Results) => void; +}; + +export type ReporterEvents = Partial>; diff --git a/src/bin/help.ts b/src/bin/help.ts index 9bf70ed3..7f1d1bd3 100644 --- a/src/bin/help.ts +++ b/src/bin/help.ts @@ -26,6 +26,10 @@ const summary: [string, string][] = [ ['--killPort', 'Terminate the specified ports.'], ['--killRange', 'Terminate the specified port ranges.'], ['--listFiles', 'Display all the files returned in the terminal.'], + [ + '--reporter, -r', + 'Specify the reporter: poku, dot, compact, focus, verbose, classic.', + ], ['--only', 'Enable selective execution of tests.'], ['--quiet, -q', 'Run tests with no logs.'], ['--sequential', 'Run tests files sequentially.'], @@ -73,7 +77,7 @@ ${sortedSummary `; const footer = ` -${b('Documentation:')} ${u('https://poku.io')} +${b('Documentation:')} ${u('https://poku.io/docs')} ${bullet} ${b('Poku')} is made with ${b('love')} and ${b('care')} in every detail. ${bullet} Give him a ${b('star')} to show your support 🌟 diff --git a/src/bin/index.ts b/src/bin/index.ts index 9ebaad1c..ae8f567f 100644 --- a/src/bin/index.ts +++ b/src/bin/index.ts @@ -1,7 +1,7 @@ #! /usr/bin/env node import { escapeRegExp } from '../modules/helpers/list-files.js'; import { getArg, getPaths, hasArg, argToArray } from '../parsers/get-arg.js'; -import { states } from '../configs/files.js'; +import { states } from '../configs/poku.js'; import { format } from '../services/format.js'; import { kill } from '../modules/helpers/kill.js'; import { envFile } from '../modules/helpers/env.js'; @@ -11,6 +11,7 @@ import { getConfigs } from '../parsers/options.js'; import { GLOBAL, VERSION } from '../configs/poku.js'; (async () => { + /* c8 ignore next 4 */ // Version is tested during build process: "../../tools/build/version.ts" if (hasArg('version') || hasArg('v', '-')) { log(VERSION); return; @@ -37,6 +38,11 @@ import { GLOBAL, VERSION } from '../configs/poku.js'; const killPort = getArg('killPort'); const killRange = getArg('killRange'); const killPID = getArg('killPid'); + const reporter = + getArg('reporter') ?? + getArg('r', '-') ?? + GLOBAL.configsFromFile.reporter ?? + 'poku'; /* c8 ignore start */ // Deno const denoAllow = argToArray('denoAllow') ?? configsFromFile?.deno?.allow; const denoDeny = argToArray('denoDeny') ?? configsFromFile?.deno?.deny; @@ -112,6 +118,7 @@ import { GLOBAL, VERSION } from '../configs/poku.js'; cjs: denoCJS, }, noExit: watchMode, + reporter, beforeEach: 'beforeEach' in configsFromFile ? configsFromFile.beforeEach : undefined, afterEach: @@ -160,15 +167,19 @@ import { GLOBAL, VERSION } from '../configs/poku.js'; if (debug || configsFromFile?.debug) { hr(); log(`${format(' Debug Enabled ').bg('brightBlue')}\n`); - log(`${format('…').info().italic()} ${format('Paths').bold()}`); - console.table(dirs); - log('\n'); log(`${format('…').info().italic()} ${format('Options').bold()}`); - console.dir(GLOBAL.configs, { depth: null, colors: true }); + console.dir(GLOBAL.configs, { + depth: Number.POSITIVE_INFINITY, + colors: true, + }); + log( + `\n${format('💡')} To list all test files, run: ${format('poku --listFiles').bold()}` + ); } await Promise.all(tasks); await poku(dirs); + /* c8 ignore next 1 */ // Blocked by TSX if (watchMode) await require('./watch.js').startWatch(dirs); })(); diff --git a/src/bin/watch.ts b/src/bin/watch.ts index d0881318..c36c4818 100644 --- a/src/bin/watch.ts +++ b/src/bin/watch.ts @@ -5,13 +5,14 @@ import { log, hr } from '../services/write.js'; import process from 'node:process'; import { format } from '../services/format.js'; import { getArg } from '../parsers/get-arg.js'; -import { fileResults } from '../configs/files.js'; import { availableParallelism } from '../polyfills/os.js'; import { GLOBAL } from '../configs/poku.js'; +import { errors } from '../services/reporters/poku.js'; export const startWatch = async (dirs: string[]) => { let isRunning = false; + const { configs } = GLOBAL; const watchers: Set = new Set(); const executing = new Set(); const interval = Number(getArg('watchInterval')) || 1500; @@ -21,8 +22,7 @@ export const startWatch = async (dirs: string[]) => { }; const resultsClear = () => { - fileResults.success.clear(); - fileResults.fail.clear(); + errors.length = 0; }; const listenStdin = async (input: Buffer | string) => { @@ -44,8 +44,8 @@ export const startWatch = async (dirs: string[]) => { const mappedTests = await mapTests( '.', dirs, - GLOBAL.configs.filter, - GLOBAL.configs.exclude + configs.filter, + configs.exclude ); for (const mappedTest of Array.from(mappedTests.keys())) { @@ -62,9 +62,9 @@ export const startWatch = async (dirs: string[]) => { if (!tests) return; await poku(Array.from(tests), { - ...GLOBAL.configs, + ...configs, concurrency: - GLOBAL.configs.concurrency ?? + configs.concurrency ?? Math.max(Math.floor(availableParallelism() / 2), 1), }); diff --git a/src/builders/reporter.ts b/src/builders/reporter.ts new file mode 100644 index 00000000..ea6ec9de --- /dev/null +++ b/src/builders/reporter.ts @@ -0,0 +1,9 @@ +import type { ReporterEvents, ReporterPlugin } from '../@types/poku.js'; +import { poku } from '../services/reporters/poku.js'; + +export const createReporter = (events: ReporterEvents): ReporterPlugin => { + return () => ({ + ...poku, + ...events, + }); +}; diff --git a/src/configs/files.ts b/src/configs/files.ts deleted file mode 100644 index ec3862f1..00000000 --- a/src/configs/files.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { FileResults } from '../@types/list-files.js'; -import type { FinalResults, States } from '../@types/poku.js'; - -export const states = Object.create(null) as States; - -export const fileResults: FileResults = { - success: new Map(), - fail: new Map(), -}; - -export const finalResults = Object.create(null) as FinalResults; diff --git a/src/configs/poku.ts b/src/configs/poku.ts index 02fe2d67..29599bf7 100644 --- a/src/configs/poku.ts +++ b/src/configs/poku.ts @@ -1,10 +1,22 @@ import { env, cwd } from 'node:process'; -import type { ConfigFile, ConfigJSONFile, Configs } from '../@types/poku.js'; +import type { Timespan, States } from '../@types/poku.js'; +import type { + ConfigFile, + ConfigJSONFile, + Configs, + Runtime, +} from '../@types/poku.js'; +import { reporter } from '../services/reporter.js'; +import { getRuntime } from '../parsers/get-runtime.js'; + +export const states = Object.create(null) as States; + +export const timespan = Object.create(null) as Timespan; export const results = { - success: 0, - fail: 0, - skip: 0, + passed: 0, + failed: 0, + skipped: 0, todo: 0, }; @@ -17,8 +29,10 @@ export const GLOBAL = { configs: Object.create(null) as Configs, configFile: undefined as string | undefined, configsFromFile: Object.create(null) as ConfigFile | ConfigJSONFile, - isPoku: typeof env?.POKU_FILE === 'string' && env?.POKU_FILE.length > 0, + reporter: reporter[env.POKU_REPORTER || 'poku'](), + isPoku: typeof env.POKU_FILE === 'string' && env.POKU_FILE.length > 0, FILE: env.POKU_FILE, envFile: undefined as string | undefined, + runtime: (env.POKU_RUNTIME || getRuntime()) as Runtime, runAsOnly: false, }; diff --git a/src/globals.d.ts b/src/globals.d.ts new file mode 100644 index 00000000..0205a01f --- /dev/null +++ b/src/globals.d.ts @@ -0,0 +1,9 @@ +declare const Deno: { + version: { + deno: string; + }; +}; + +declare const Bun: { + version: string; +}; diff --git a/src/modules/essentials/poku.ts b/src/modules/essentials/poku.ts index b6082e91..4a1b169a 100644 --- a/src/modules/essentials/poku.ts +++ b/src/modules/essentials/poku.ts @@ -1,18 +1,16 @@ import type { Code } from '../../@types/code.js'; import type { Configs } from '../../@types/poku.js'; import process from 'node:process'; -import { log, hr } from '../../services/write.js'; import { exit } from '../helpers/exit.js'; -import { format, showTestResults } from '../../services/format.js'; -import { finalResults } from '../../configs/files.js'; +import { results, timespan } from '../../configs/poku.js'; import { runTests } from '../../services/run-tests.js'; import { GLOBAL } from '../../configs/poku.js'; +import { reporter } from '../../services/reporter.js'; -/* c8 ignore start */ // Process-based +/* c8 ignore next 1 */ // Process-based export const onSigint = () => process.stdout.write('\u001B[?25h'); process.once('SIGINT', onSigint); -/* c8 ignore stop */ export async function poku( targetPaths: string | string[], @@ -30,31 +28,32 @@ export async function poku( if (configs) GLOBAL.configs = { ...GLOBAL.configs, ...configs }; - finalResults.started = new Date(); + timespan.started = new Date(); const start = process.hrtime(); - const dirs = Array.prototype.concat(targetPaths); + const paths: string[] = Array.prototype.concat(targetPaths); const showLogs = !GLOBAL.configs.quiet; + const { reporter: plugin } = GLOBAL.configs; - if (showLogs) { - hr(); - log(`${format('Running Tests').bold()}\n`); - } + if (typeof plugin === 'string' && plugin !== 'poku') + GLOBAL.reporter = reporter[plugin](); + + if (showLogs) GLOBAL.reporter.onRunStart(); try { - const promises = dirs.map(async (dir) => await runTests(dir)); + const promises = paths.map(async (dir) => await runTests(dir)); const concurrency = await Promise.all(promises); if (concurrency.some((result) => !result)) code = 1; } finally { const end = process.hrtime(start); - const total = (end[0] * 1e3 + end[1] / 1e6).toFixed(6); + const total = end[0] * 1e3 + end[1] / 1e6; - finalResults.time = total; + timespan.duration = total; + timespan.finished = new Date(); } - showLogs && showTestResults(); - + if (showLogs) GLOBAL.reporter.onRunResult({ code, timespan, results }); if (GLOBAL.configs.noExit) return code; exit(code, GLOBAL.configs.quiet); diff --git a/src/modules/essentials/strict.ts b/src/modules/essentials/strict.ts index dab71349..72ce33c8 100644 --- a/src/modules/essentials/strict.ts +++ b/src/modules/essentials/strict.ts @@ -1,9 +1,10 @@ import { createAssert } from '../../builders/assert.js'; -import { nodeVersion } from '../../parsers/get-runtime.js'; +import { GLOBAL } from '../../configs/poku.js'; +import { runtimeVersion } from '../../parsers/runtime-version.js'; /* c8 ignore next 4 */ // Platform version const nodeAssert = - !nodeVersion || nodeVersion >= 16 + GLOBAL.runtime !== 'node' || runtimeVersion >= 16 ? require('node:assert/strict') : require('node:assert'); diff --git a/src/modules/helpers/create-service.ts b/src/modules/helpers/create-service.ts index 91c0cd12..1d9357da 100644 --- a/src/modules/helpers/create-service.ts +++ b/src/modules/helpers/create-service.ts @@ -5,11 +5,12 @@ import type { } from '../../@types/background-process.js'; import process from 'node:process'; import { spawn } from 'node:child_process'; -import { isWindows, runner, scriptRunner } from '../../parsers/get-runner.js'; +import { runner, scriptRunner } from '../../parsers/get-runner.js'; import { normalize } from 'node:path'; import { sanitizePath } from './list-files.js'; import { kill } from './kill.js'; import { log } from '../../services/write.js'; +import { isWindows } from '../../parsers/os.js'; const runningProcesses: Map = new Map(); diff --git a/src/modules/helpers/describe.ts b/src/modules/helpers/describe.ts index c79a77f4..6694e45e 100644 --- a/src/modules/helpers/describe.ts +++ b/src/modules/helpers/describe.ts @@ -1,8 +1,5 @@ import type { DescribeOptions } from '../../@types/describe.js'; import { hrtime } from 'node:process'; -import { format } from '../../services/format.js'; -import { log } from '../../services/write.js'; -import { indentation } from '../../configs/indentation.js'; import { todo, skip, onlyDescribe } from './modifiers.js'; import { hasOnly } from '../../parsers/get-arg.js'; import { checkOnly } from '../../parsers/callback.js'; @@ -16,6 +13,8 @@ export async function describeBase( let cb: (() => unknown | Promise) | undefined; let options: DescribeOptions | undefined; + const { reporter } = GLOBAL; + if (typeof arg1 === 'string') { title = arg1; @@ -26,27 +25,17 @@ export async function describeBase( options = arg2 as DescribeOptions; } + const hasCB = typeof cb === 'function'; + if (title) { - indentation.hasDescribe = true; - - const { background, icon } = - options ?? (Object.create(null) as DescribeOptions); - const message = `${cb ? format('◌').dim() : (icon ?? '☰')} ${cb ? format(title).dim() : format(title).bold()}`; - const noBackground = !background; - - if (noBackground) log(format(message).bold()); - else - log( - format(` ${message} `).bg( - typeof background === 'string' ? background : 'grey' - ) - ); + if (hasCB) reporter.onDescribeStart({ title }); + else reporter.onDescribeAsTitle(title, options as DescribeOptions); } - if (typeof cb !== 'function') return; + if (!hasCB) return; const start = hrtime(); - const resultCb = cb(); + const resultCb = cb!(); if (resultCb instanceof Promise) await resultCb; @@ -54,13 +43,11 @@ export async function describeBase( if (!title) return; - const total = (end[0] * 1e3 + end[1] / 1e6).toFixed(6); + const duration = end[0] * 1e3 + end[1] / 1e6; + + reporter.onDescribeEnd({ title, duration }); GLOBAL.runAsOnly = false; - indentation.hasDescribe = false; - log( - `${format(`● ${title}`).success().bold()} ${format(`› ${total}ms`).success().dim()}` - ); } async function describeCore( diff --git a/src/modules/helpers/exit.ts b/src/modules/helpers/exit.ts index 1247dedb..a0fa24f2 100644 --- a/src/modules/helpers/exit.ts +++ b/src/modules/helpers/exit.ts @@ -1,64 +1,22 @@ import type { Code } from '../../@types/code.js'; import process from 'node:process'; -import { results } from '../../configs/poku.js'; -import { format } from '../../services/format.js'; -import { log, hr } from '../../services/write.js'; -import { fileResults, finalResults } from '../../configs/files.js'; -import { parseTime, parseTimeToSecs } from '../../parsers/time.js'; +import { GLOBAL, results } from '../../configs/poku.js'; +import { timespan } from '../../configs/poku.js'; import { AssertionError } from 'node:assert'; export const exit = (code: Code, quiet?: boolean) => { - const isPoku = results.success > 0 || results.fail > 0; - const success = ` PASS › ${results.success} `; - const failure = ` FAIL › ${results.fail} `; - const skips = ` SKIP › ${results.skip} `; - const plans = ` TODO › ${results.todo} `; - const inline = results.skip === 0 || results.todo === 0; + const isPoku = results.passed > 0 || results.failed > 0; - let message = ''; - - if (inline) { - message += `${format(success).bg('green')} ${format(failure).bg(results.fail === 0 ? 'grey' : 'brightRed')}`; - - if (results.skip) message += ` ${format(skips).bg('brightBlue')}`; - if (results.todo) message += ` ${format(plans).bg('brightBlue')}`; - } else { - message += `${format(success).success().bold()}\n`; - message += - results.fail === 0 - ? format(`${failure}\n`).bold() - : `${format(failure).fail().bold()}\n`; - message += `${format(skips).info().bold()}\n`; - message += `${format(plans).info().bold()}`; - } - - !quiet && - process.on('exit', (code) => { - if (isPoku) { - hr(); - log( - ` ${format(`Start at › ${format(`${parseTime(finalResults.started)}`).bold()}`).dim()}` - ); - log( - ` ${format('Duration ›').dim()} ${format(`${finalResults.time}ms`).bold().dim()} ${format(`(±${parseTimeToSecs(finalResults.time)} seconds)`).dim()}` - ); - log( - ` ${format(`Test Files › ${format(String(fileResults.success.size + fileResults.fail.size)).bold()}`).dim()}` - ); - hr(); - log(message); - hr(); - } - - log( - `${format('Exited with code').dim()} ${format(String(code)).bold()[code === 0 ? 'success' : 'fail']()}\n` - ); + if (!quiet && isPoku) + GLOBAL.reporter.onExit({ + code, + timespan, + results, }); process.exitCode = code === 0 ? 0 : 1; }; -/* c8 ignore start */ // Unknown external error process.on('unhandledRejection', (err) => { if (!(err instanceof AssertionError)) console.error('unhandledRejection', err); @@ -66,9 +24,9 @@ process.on('unhandledRejection', (err) => { process.exitCode = 1; }); +/* c8 ignore next 5 */ // Unknown external error process.on('uncaughtException', (err) => { if (!(err instanceof AssertionError)) console.error('uncaughtException', err); process.exitCode = 1; }); -/* c8 ignore stop */ diff --git a/src/modules/helpers/get-pids.ts b/src/modules/helpers/get-pids.ts index cf8b503c..fe078727 100644 --- a/src/modules/helpers/get-pids.ts +++ b/src/modules/helpers/get-pids.ts @@ -1,4 +1,4 @@ -import { isWindows } from '../../parsers/get-runner.js'; +import { isWindows } from '../../parsers/os.js'; import { getPIDs as getPIDsService, populateRange, diff --git a/src/modules/helpers/it/core.ts b/src/modules/helpers/it/core.ts index 1f1668de..c8f2d2b8 100644 --- a/src/modules/helpers/it/core.ts +++ b/src/modules/helpers/it/core.ts @@ -1,8 +1,6 @@ import { hrtime } from 'node:process'; import { each } from '../../../configs/each.js'; import { indentation } from '../../../configs/indentation.js'; -import { format } from '../../../services/format.js'; -import { log } from '../../../services/write.js'; import { todo, skip, onlyIt } from '../modifiers.js'; import { hasOnly } from '../../../parsers/get-arg.js'; import { GLOBAL } from '../../../configs/poku.js'; @@ -14,21 +12,15 @@ export async function itBase( ] ): Promise { try { - let message: string | undefined; + let title: string | undefined; let cb: () => unknown | Promise; if (typeof args[0] === 'string') { - message = args[0]; + title = args[0]; cb = args[1] as () => unknown | Promise; } else cb = args[0] as () => unknown | Promise; - if (message) { - indentation.hasItOrTest = true; - - log( - `${indentation.hasDescribe ? ' ' : ''}${format(`◌ ${message}`).dim()}` - ); - } + GLOBAL.reporter.onItStart({ title }); if (typeof each.before.cb === 'function') { const beforeResult = each.before.cb(); @@ -49,14 +41,11 @@ export async function itBase( if (afterResult instanceof Promise) await afterResult; } - if (!message) return; + if (!title) return; - const total = (end[0] * 1e3 + end[1] / 1e6).toFixed(6); + const duration = end[0] * 1e3 + end[1] / 1e6; - indentation.hasItOrTest = false; - log( - `${indentation.hasDescribe ? ' ' : ''}${format(`● ${message}`).success().bold()} ${format(`› ${total}ms`).success().dim()}` - ); + GLOBAL.reporter.onItEnd({ title, duration }); } catch (error) { indentation.hasItOrTest = false; @@ -70,28 +59,25 @@ export async function itBase( } } -async function itCore( - message: string, - cb: () => Promise -): Promise; -function itCore(message: string, cb: () => unknown): void; +async function itCore(title: string, cb: () => Promise): Promise; +function itCore(title: string, cb: () => unknown): void; async function itCore(cb: () => Promise): Promise; function itCore(cb: () => unknown): void; async function itCore( - messageOrCb: string | (() => unknown) | (() => Promise), + titleOrCb: string | (() => unknown) | (() => Promise), cb?: (() => unknown) | (() => Promise) ): Promise { if (hasOnly) { if (!GLOBAL.runAsOnly) return; - if (typeof messageOrCb === 'string' && typeof cb === 'function') - return itBase(messageOrCb, cb); + if (typeof titleOrCb === 'string' && typeof cb === 'function') + return itBase(titleOrCb, cb); - if (typeof messageOrCb === 'function') return itBase(messageOrCb); + if (typeof titleOrCb === 'function') return itBase(titleOrCb); } - if (typeof messageOrCb === 'string' && cb) return itBase(messageOrCb, cb); - if (typeof messageOrCb === 'function') return itBase(messageOrCb); + if (typeof titleOrCb === 'string' && cb) return itBase(titleOrCb, cb); + if (typeof titleOrCb === 'function') return itBase(titleOrCb); } export const it = Object.assign(itCore, { diff --git a/src/modules/helpers/kill.ts b/src/modules/helpers/kill.ts index caba2b46..28156c26 100644 --- a/src/modules/helpers/kill.ts +++ b/src/modules/helpers/kill.ts @@ -1,4 +1,4 @@ -import { isWindows } from '../../parsers/get-runner.js'; +import { isWindows } from '../../parsers/os.js'; import { killPID as killPIDService, setPortsAndPIDs, diff --git a/src/modules/helpers/list-files.ts b/src/modules/helpers/list-files.ts index 505d45c4..0d52ecd0 100644 --- a/src/modules/helpers/list-files.ts +++ b/src/modules/helpers/list-files.ts @@ -2,7 +2,7 @@ import type { Configs } from '../../@types/list-files.js'; import { env } from 'node:process'; import { sep, join } from 'node:path'; import { readdir, stat as fsStat } from 'node:fs/promises'; -import { states } from '../../configs/files.js'; +import { states } from '../../configs/poku.js'; const regex = { sep: /[/\\]+/g, diff --git a/src/modules/helpers/modifiers.ts b/src/modules/helpers/modifiers.ts index 169e123e..c62e83b7 100644 --- a/src/modules/helpers/modifiers.ts +++ b/src/modules/helpers/modifiers.ts @@ -1,6 +1,5 @@ import { exit } from 'node:process'; import { log } from '../../services/write.js'; -import { indentation } from '../../configs/indentation.js'; import { format } from '../../services/format.js'; import { itBase } from './it/core.js'; import { describeBase } from './describe.js'; @@ -15,12 +14,12 @@ export async function todo( ): Promise; export function todo(message: string, cb?: () => unknown): void; export async function todo( - message: string | (() => unknown) | (() => Promise), + messageOrCb: string | (() => unknown) | (() => Promise), _cb?: (() => unknown) | (() => Promise) ): Promise { - log( - `${indentation.hasDescribe ? ' ' : ''}${format(`● ${message}`).cyan().bold()}` - ); + const message = typeof messageOrCb === 'string' ? messageOrCb : 'Planning'; + + GLOBAL.reporter.onTodoModifier({ message }); } export async function skip( @@ -36,9 +35,7 @@ export async function skip( ): Promise { const message = typeof messageOrCb === 'string' ? messageOrCb : 'Skipping'; - log( - `${indentation.hasDescribe ? ' ' : ''}${format(`◯ ${message}`).info().bold()}` - ); + GLOBAL.reporter.onSkipModifier({ message }); } export async function onlyDescribe( diff --git a/src/modules/helpers/skip.ts b/src/modules/helpers/skip.ts index e095405e..c107d464 100644 --- a/src/modules/helpers/skip.ts +++ b/src/modules/helpers/skip.ts @@ -1,21 +1,8 @@ import { exit } from 'node:process'; -import { log } from '../../services/write.js'; -import { format } from '../../services/format.js'; import { GLOBAL } from '../../configs/poku.js'; export const skip = (message = 'Skipping') => { - const { isPoku, FILE } = GLOBAL; - - if (message) - log( - format( - isPoku - ? `◯ ${message} ${format('›').dim()} ${format(`${FILE}`).italic().gray().dim()}` - : `◯ ${message}` - ) - .info() - .bold() - ); + if (message) GLOBAL.reporter.onSkipFile({ message }); exit(0); }; diff --git a/src/parsers/get-runner.ts b/src/parsers/get-runner.ts index bbc8d296..cdab64ba 100644 --- a/src/parsers/get-runner.ts +++ b/src/parsers/get-runner.ts @@ -1,25 +1,22 @@ import type { Runner } from '../@types/runner.js'; -import { platform } from 'node:process'; import { extname } from 'node:path'; -import { getRuntime } from './get-runtime.js'; import { GLOBAL } from '../configs/poku.js'; - -export const isWindows = platform === 'win32'; +import { isWindows } from './os.js'; export const runner = (filename: string): string[] => { - const runtime = getRuntime(); + const { configs, runtime } = GLOBAL; if (runtime === 'bun') return ['bun']; if (runtime === 'deno') { - const denoAllow = GLOBAL.configs.deno?.allow - ? GLOBAL.configs.deno.allow + const denoAllow = configs.deno?.allow + ? configs.deno.allow .map((allow) => (allow ? `--allow-${allow}` : '')) .filter((allow) => allow) : ['--allow-read', '--allow-env', '--allow-run', '--allow-net']; - const denoDeny = GLOBAL.configs.deno?.deny - ? GLOBAL.configs.deno.deny + const denoDeny = configs.deno?.deny + ? configs.deno.deny .map((deny) => (deny ? `--deny-${deny}` : '')) .filter((deny) => deny) : []; diff --git a/src/parsers/get-runtime.ts b/src/parsers/get-runtime.ts index d4532f78..9dcbaae9 100644 --- a/src/parsers/get-runtime.ts +++ b/src/parsers/get-runtime.ts @@ -1,18 +1,24 @@ +import { env } from 'node:process'; +import { basename } from 'node:path'; import type { Runtime } from '../@types/poku.js'; -import { version, env } from 'node:process'; -declare const Deno: unknown; -declare const Bun: unknown; +export const getRuntime = (): Runtime => { + const { _, POKU_RUNTIME } = env; -const regex = /v(\d+)\./; + if (POKU_RUNTIME) return POKU_RUNTIME as Runtime; -export const getRuntime = (): Runtime => { - if (env.POKU_RUNTIME) return env.POKU_RUNTIME as Runtime; + // Unix + if (typeof _ === 'string') { + const bin = basename(_); + + if (bin.indexOf('bun') !== -1) return 'bun'; + if (bin.indexOf('deno') !== -1) return 'deno'; + if (bin.indexOf('node') !== -1 || bin.indexOf('tsx') !== -1) return 'node'; + } + + // Win32 if (typeof Deno !== 'undefined') return 'deno'; if (typeof Bun !== 'undefined') return 'bun'; return 'node'; }; - -export const nodeVersion = - getRuntime() === 'node' ? Number(version.match(regex)?.[1]) : undefined; diff --git a/src/parsers/options.ts b/src/parsers/options.ts index c4e6d677..f255a450 100644 --- a/src/parsers/options.ts +++ b/src/parsers/options.ts @@ -3,7 +3,7 @@ import { normalize, join } from 'node:path'; import { readFile } from 'node:fs/promises'; import { JSONC } from '../polyfills/jsonc.js'; import { GLOBAL } from '../configs/poku.js'; -import { isWindows } from './get-runner.js'; +import { isWindows } from './os.js'; export const getConfigs = async ( customPath?: string diff --git a/src/parsers/os.ts b/src/parsers/os.ts new file mode 100644 index 00000000..729c07bf --- /dev/null +++ b/src/parsers/os.ts @@ -0,0 +1,3 @@ +import { platform } from 'node:process'; + +export const isWindows = platform === 'win32'; diff --git a/src/parsers/output.ts b/src/parsers/output.ts index b6796aab..c60031b3 100644 --- a/src/parsers/output.ts +++ b/src/parsers/output.ts @@ -11,7 +11,7 @@ export const parserOutput = (options: { output: string; result: boolean }) => { const normalizedOutput = JSON.stringify(output); const hasSkip = normalizedOutput.match(regex.skip); - if (hasSkip) results.skip += hasSkip.length; + if (hasSkip) results.skipped += hasSkip.length; const hasTodo = normalizedOutput.match(regex.todo); if (hasTodo) results.todo += hasTodo.length; diff --git a/src/parsers/runtime-version.ts b/src/parsers/runtime-version.ts new file mode 100644 index 00000000..3d0dcf60 --- /dev/null +++ b/src/parsers/runtime-version.ts @@ -0,0 +1,8 @@ +import { version } from 'node:process'; +import { GLOBAL } from '../configs/poku.js'; + +export const runtimeVersion: number = (() => { + if (GLOBAL.runtime === 'bun') return Number(Bun.version.split('.')[0]); + if (GLOBAL.runtime === 'deno') return Number(Deno.version.deno.split('.')[0]); + return Number(version.replace('v', '').split('.')[0]); +})(); diff --git a/src/parsers/time.ts b/src/parsers/time.ts index 92de6714..996d3600 100644 --- a/src/parsers/time.ts +++ b/src/parsers/time.ts @@ -8,8 +8,5 @@ export const parseTime = (date: Date): string => { return `${hours}:${minutes}:${seconds}`; }; -export const parseTimeToSecs = (milliseconds: string): string => { - const ms = Number(milliseconds); - - return (ms / 1000).toFixed(2); -}; +export const parseTimeToSecs = (milliseconds: number): string => + (milliseconds / 1000).toFixed(2); diff --git a/src/services/assert.ts b/src/services/assert.ts index e73f9ac4..f4989002 100644 --- a/src/services/assert.ts +++ b/src/services/assert.ts @@ -1,101 +1,22 @@ import type { ProcessAssertionOptions } from '../@types/assert.js'; import { AssertionError } from 'node:assert'; import process from 'node:process'; -import path from 'node:path'; -import { findFile } from '../parsers/find-file-from-stack.js'; -import { parseResultType } from '../parsers/assert.js'; -import { indentation } from '../configs/indentation.js'; -import { format } from './format.js'; -import { log, hr } from './write.js'; import { GLOBAL } from '../configs/poku.js'; -const { cwd } = GLOBAL; -const regexFile = /file:(\/\/)?/; - const assertProcessor = () => { - const { isPoku, FILE } = GLOBAL; - - let preIdentation = ''; - - const handleSuccess = (options: ProcessAssertionOptions) => { - if (typeof options.message === 'string') { - if (indentation.hasDescribe) preIdentation += ' '; - if (indentation.hasItOrTest) preIdentation += ' '; - - const message = - isPoku && !indentation.hasDescribe && !indentation.hasItOrTest - ? `${preIdentation}${format(`${format(`✔ ${options.message}`).bold()} ${format(`› ${FILE}`).success().dim()}`).success()}` - : `${preIdentation}${format(`✔ ${options.message}`).success().bold()}`; - - log(message); - } + const { isPoku, reporter } = GLOBAL; - preIdentation = ''; + const handleSuccess = ({ message }: ProcessAssertionOptions) => { + if (typeof message === 'string') reporter.onAssertionSuccess({ message }); }; const handleError = (error: unknown, options: ProcessAssertionOptions) => { process.exitCode = 1; - if (error instanceof AssertionError) { - const { code, actual, expected, operator } = error; - const absolutePath = findFile(error).replace(regexFile, ''); - const file = path.relative(path.resolve(cwd), absolutePath); - - if (indentation.hasDescribe) preIdentation += ' '; - if (indentation.hasItOrTest) preIdentation += ' '; - - let message = ''; - - if (typeof options.message === 'string') message = options.message; - else if (options.message instanceof Error) - message = options.message.message; - else if (typeof options.defaultMessage === 'string') - message = options.defaultMessage; - - const finalMessage = - message?.trim().length > 0 - ? format(`✘ ${message}`).fail().bold() - : format('✘ Assertion Error').fail().bold(); - - log( - isPoku - ? `${preIdentation}${finalMessage} ${format(`› ${FILE}`).fail().dim()}` - : `${preIdentation}${finalMessage}` - ); - - file && log(`${format(`${preIdentation} File`).dim()} ${file}`); - log(`${format(`${preIdentation} Code`).dim()} ${code}`); - log(`${format(`${preIdentation} Operator`).dim()} ${operator}\n`); - - if (!options?.hideDiff) { - const splitActual = parseResultType(actual).split('\n'); - const splitExpected = parseResultType(expected).split('\n'); - - log(format(`${preIdentation} ${options?.actual ?? 'Actual'}:`).dim()); - - for (const line of splitActual) - log(`${preIdentation} ${format(line).fail().bold()}`); - - log( - `\n${preIdentation} ${format(`${options?.expected ?? 'Expected'}:`).dim()}` - ); - - for (const line of splitExpected) - log(`${preIdentation} ${format(line).success().bold()}`); - - preIdentation = ''; - } - - if (options.throw) { - console.error(error); - hr(); - } - - if (isPoku) throw error; - } + if (error instanceof AssertionError) + reporter.onAssertionFailure({ assertOptions: options, error }); - /* c8 ignore next */ // Unknown external error - throw error; + if (isPoku) throw error; }; const processAssert = (cb: () => void, options: ProcessAssertionOptions) => { diff --git a/src/services/container.ts b/src/services/container.ts index 1dab1a59..93e6a40f 100644 --- a/src/services/container.ts +++ b/src/services/container.ts @@ -4,7 +4,7 @@ import type { } from '../@types/container.js'; import { spawn, type SpawnOptionsWithoutStdio } from 'node:child_process'; import { log } from '../services/write.js'; -import { isWindows } from '../parsers/get-runner.js'; +import { isWindows } from '../parsers/os.js'; import { sanitizePath } from '../modules/helpers/list-files.js'; const runDockerCommand = ( @@ -12,8 +12,8 @@ const runDockerCommand = ( args: string[], options?: SpawnOptionsWithoutStdio, verbose?: boolean -): Promise => { - return new Promise((resolve) => { +): Promise => + new Promise((resolve) => { const dockerProcess = spawn(command, args, { ...options, shell: isWindows, @@ -29,7 +29,6 @@ const runDockerCommand = ( dockerProcess.on('close', (code) => resolve(code === 0)); dockerProcess.on('error', () => resolve(false)); }); -}; export class DockerContainer { private file; diff --git a/src/services/each.ts b/src/services/each.ts index 793f7726..cddc8925 100644 --- a/src/services/each.ts +++ b/src/services/each.ts @@ -7,10 +7,12 @@ const eachCore = async ( type: keyof Required>, fileRelative: string ): Promise => { - if (typeof GLOBAL.configs?.[type] !== 'function') return true; + const { configs } = GLOBAL; - const cb = GLOBAL.configs[type]; - const showLogs = !GLOBAL.configs.quiet; + if (typeof configs?.[type] !== 'function') return true; + + const cb = configs[type]; + const showLogs = !configs.quiet; const cbName = cb.name !== type ? cb.name : 'anonymous function'; showLogs && diff --git a/src/services/format.ts b/src/services/format.ts index 76ed4cb7..3b6e56c1 100644 --- a/src/services/format.ts +++ b/src/services/format.ts @@ -1,7 +1,3 @@ -import { fileResults } from '../configs/files.js'; -import { indentation } from '../configs/indentation.js'; -import { log, hr } from '../services/write.js'; - export const backgroundColor = { white: 7, black: 40, @@ -30,7 +26,7 @@ export class Formatter { this.text = text; } - private code(code: string) { + code(code: string) { this.parts += `${ESC}${code}m`; return this; } @@ -94,31 +90,3 @@ export const format = (text: string) => Formatter.create(text); export const getLargestStringLength = (arr: string[]): number => arr.reduce((max, current) => Math.max(max, current.length), 0); - -export const showTestResults = () => { - hr(); - - if (fileResults.success.size > 0 && fileResults.fail.size === 0) { - log( - Array.from(fileResults.success) - .map( - ([file, time]) => - `${indentation.test}${format('✔').success()} ${format(`${file} ${format(`› ${time}ms`).success()}`).dim()}` - ) - .join('\n') - ); - - return; - } - - if (fileResults.fail.size > 0) { - log( - Array.from(fileResults.fail) - .map( - ([file, time]) => - `${indentation.test}${format('✘').fail()} ${format(`${file} ${format(`› ${time}ms`).fail()}`).dim()}` - ) - .join('\n') - ); - } -}; diff --git a/src/services/reporter.ts b/src/services/reporter.ts new file mode 100644 index 00000000..00b51159 --- /dev/null +++ b/src/services/reporter.ts @@ -0,0 +1,16 @@ +import type { Reporter, ReporterPlugin } from '../@types/poku.js'; +import { poku } from './reporters/poku.js'; +import { dot } from './reporters/dot.js'; +import { compact } from './reporters/compact.js'; +import { focus } from './reporters/focus.js'; +import { verbose } from './reporters/verbose.js'; +import { classic } from './reporters/classic.js'; + +export const reporter: Record = { + poku: () => poku, + dot, + compact, + focus, + verbose, + classic, +}; diff --git a/src/services/reporters/classic.ts b/src/services/reporters/classic.ts new file mode 100644 index 00000000..34ed236d --- /dev/null +++ b/src/services/reporters/classic.ts @@ -0,0 +1,62 @@ +import type { ReporterPlugin } from '../../@types/poku.js'; +import { indentation } from '../../configs/indentation.js'; +import { log, hr } from '../write.js'; +import { format } from '../format.js'; +import { createReporter } from '../../builders/reporter.js'; +import { poku } from './poku.js'; + +export const classic: ReporterPlugin = (() => { + const files = { + passed: new Map(), + failed: new Map(), + }; + + return createReporter({ + onRunStart() { + hr(); + log(`${format('Running Tests').bold()}\n`); + }, + onFileStart() {}, + onFileResult({ status, path, duration, output }) { + if (status) files.passed.set(path.relative, duration); + else files.failed.set(path.relative, duration); + + if (output) log(output); + }, + onRunResult() { + hr(); + + if (files.passed.size > 0 && files.failed.size === 0) { + log( + Array.from(files.passed) + .map( + ([file, time]) => + `${indentation.test}${format('✔').success()} ${format(`${file} ${format(`› ${time.toFixed(6)}ms`).success()}`).dim()}` + ) + .join('\n') + ); + + return; + } + + if (files.failed.size > 0) { + log( + Array.from(files.failed) + .map( + ([file, time]) => + `${indentation.test}${format('✘').fail()} ${format(`${file} ${format(`› ${time.toFixed(6)}ms`).fail()}`).dim()}` + ) + .join('\n') + ); + } + }, + onExit(options) { + const { code } = options; + + poku.onExit(options); + log( + `${format('Exited with code').dim()} ${format(String(code)).bold()[code === 0 ? 'success' : 'fail']()}\n` + ); + }, + }); +})(); diff --git a/src/services/reporters/compact.ts b/src/services/reporters/compact.ts new file mode 100644 index 00000000..b03b52c5 --- /dev/null +++ b/src/services/reporters/compact.ts @@ -0,0 +1,51 @@ +import type { ReporterPlugin } from '../../@types/poku.js'; +import { hr, log } from '../write.js'; +import { format } from '../format.js'; +import { createReporter } from '../../builders/reporter.js'; +import { errors } from './poku.js'; +import { parseTimeToSecs } from '../../parsers/time.js'; + +export const compact: ReporterPlugin = (() => { + let countFails = 0; + + return createReporter({ + onRunStart() {}, + onFileStart() {}, + onDescribeAsTitle() {}, + onDescribeStart() {}, + onDescribeEnd() {}, + onItStart() {}, + onItEnd() {}, + onAssertionSuccess() {}, + onSkipFile() {}, + onSkipModifier() {}, + onTodoModifier() {}, + onFileResult({ status, path, output }) { + log( + `${status ? format(' PASS ').bg('brightGreen') : format(' FAIL ').bg('brightRed')} ${path.relative}` + ); + + if (!status) { + countFails++; + + errors.push({ + file: path.relative, + output, + }); + } + }, + onExit({ timespan, results }) { + if (countFails > 0) hr(); + + log( + `${format(String(results.passed)).bold().dim()} ${format('test file(s) passed').dim()}` + ); + log( + `${format(String(results.failed)).bold().dim()} ${format('test file(s) failed').dim()}` + ); + log( + `${format(`Finished in ±${parseTimeToSecs(timespan.duration)} seconds`).dim()}` + ); + }, + }); +})(); diff --git a/src/services/reporters/dot.ts b/src/services/reporters/dot.ts new file mode 100644 index 00000000..c4c8f8a4 --- /dev/null +++ b/src/services/reporters/dot.ts @@ -0,0 +1,33 @@ +import type { ReporterPlugin } from '../../@types/poku.js'; +import { createReporter } from '../../builders/reporter.js'; +import { hr } from '../write.js'; +import { format } from '../format.js'; +import { stdout } from 'node:process'; +import { poku, errors } from './poku.js'; + +export const dot: ReporterPlugin = (() => { + return createReporter({ + onRunStart() { + hr(); + }, + onDescribeAsTitle() {}, + onTodoModifier() {}, + onSkipModifier() {}, + onSkipFile() {}, + onFileResult({ path, status, output }) { + stdout.write( + status ? String(format('.')) : String(format('F').bold().code('31')) + ); + + if (!status) + errors.push({ + file: path.relative, + output, + }); + }, + onRunResult(options) { + stdout.write('\n'); + poku.onRunResult(options); + }, + }); +})(); diff --git a/src/services/reporters/focus.ts b/src/services/reporters/focus.ts new file mode 100644 index 00000000..3df23eaa --- /dev/null +++ b/src/services/reporters/focus.ts @@ -0,0 +1,42 @@ +import type { ReporterPlugin } from '../../@types/poku.js'; +import { createReporter } from '../../builders/reporter.js'; +import { hr, log } from '../write.js'; +import { format } from '../format.js'; +import { parseTimeToSecs } from '../../parsers/time.js'; + +export const focus: ReporterPlugin = (() => { + let countFails = 0; + + return createReporter({ + onRunStart() {}, + onDescribeAsTitle() {}, + onDescribeStart() {}, + onDescribeEnd() {}, + onItStart() {}, + onItEnd() {}, + onAssertionSuccess() {}, + onTodoModifier() {}, + onSkipModifier() {}, + onSkipFile() {}, + onFileResult({ status, output }) { + if (!status) { + countFails++; + + if (output) log(output); + } + }, + onExit({ timespan, results }) { + if (countFails > 0) hr(); + + log( + `${format(String(results.passed)).bold().dim()} ${format('test file(s) passed').dim()}` + ); + log( + `${format(String(results.failed)).bold().dim()} ${format('test file(s) failed').dim()}` + ); + log( + `${format(`Finished in ±${parseTimeToSecs(timespan.duration)} seconds`).dim()}` + ); + }, + }); +})(); diff --git a/src/services/reporters/poku.ts b/src/services/reporters/poku.ts new file mode 100644 index 00000000..50c0a94f --- /dev/null +++ b/src/services/reporters/poku.ts @@ -0,0 +1,244 @@ +import type { ReporterPlugin } from '../../@types/poku.js'; +import { indentation } from '../../configs/indentation.js'; +import { parseTime, parseTimeToSecs } from '../../parsers/time.js'; +import { log, hr } from '../write.js'; +import { format } from '../format.js'; +import { GLOBAL } from '../../configs/poku.js'; +import { findFile } from '../../parsers/find-file-from-stack.js'; +import { relative, resolve } from 'node:path'; +import { parseResultType } from '../../parsers/assert.js'; +import type { DescribeOptions } from '../../@types/describe.js'; +import { stdout } from 'node:process'; + +const regexFile = /file:(\/\/)?/; + +export const errors: { file: string; output?: string }[] = []; + +export const poku: ReturnType = (() => { + return { + onRunStart() {}, + onFileStart() {}, + onDescribeAsTitle(title, options) { + const { background, icon } = + options ?? (Object.create(null) as DescribeOptions); + + const message = `${icon ?? '☰'} ${format(title).bold()}`; + const noBackground = !background; + + if (noBackground) log(format(message).bold()); + else + log( + format(` ${message} `).bg( + typeof background === 'string' ? background : 'grey' + ) + ); + }, + onDescribeStart({ title }) { + indentation.hasDescribe = true; + + log(format(`◌ ${title}`).bold().dim()); + }, + onDescribeEnd({ title, duration }) { + indentation.hasDescribe = false; + + log( + `${format(`● ${title}`).success().bold()} ${format( + `› ${duration.toFixed(6)}ms` + ) + .success() + .dim()}` + ); + }, + onItStart({ title }) { + if (title) { + indentation.hasItOrTest = true; + + log( + `${indentation.hasDescribe ? ' ' : ''}${format(`◌ ${title}`).dim()}` + ); + } + }, + onItEnd({ title, duration }) { + indentation.hasItOrTest = false; + + log( + `${indentation.hasDescribe ? ' ' : ''}${format(`● ${title}`).success().bold()} ${format( + `› ${duration.toFixed(6)}ms` + ) + .success() + .dim()}` + ); + }, + onAssertionSuccess({ message }) { + let preIdentation = ''; + + if (indentation.hasDescribe) preIdentation += ' '; + if (indentation.hasItOrTest) preIdentation += ' '; + + const output = `${preIdentation}${format(`✔ ${message}`).success().bold()}`; + + log(output); + }, + onAssertionFailure({ assertOptions: options, error }) { + const { cwd } = GLOBAL; + + let preIdentation = ''; + + const { code, actual, expected, operator } = error; + const absolutePath = findFile(error).replace(regexFile, ''); + const file = relative(resolve(cwd), absolutePath); + + if (indentation.hasDescribe) preIdentation += ' '; + if (indentation.hasItOrTest) preIdentation += ' '; + + let message = ''; + + if (typeof options.message === 'string') message = options.message; + else if (options.message instanceof Error) + message = options.message.message; + else if (typeof options.defaultMessage === 'string') + message = options.defaultMessage; + + const output = + message?.trim().length > 0 + ? format(`✘ ${message}`).fail().bold() + : format('✘ Assertion Error').fail().bold(); + + log(`${preIdentation}${output}`); + + file && log(`${format(`${preIdentation} File`).dim()} ${file}`); + log(`${format(`${preIdentation} Code`).dim()} ${code}`); + log(`${format(`${preIdentation} Operator`).dim()} ${operator}\n`); + + if (!options?.hideDiff) { + const splitActual = parseResultType(actual).split('\n'); + const splitExpected = parseResultType(expected).split('\n'); + + log(format(`${preIdentation} ${options?.actual ?? 'Actual'}:`).dim()); + + for (const line of splitActual) + log(`${preIdentation} ${format(line).fail().bold()}`); + + log( + `\n${preIdentation} ${format(`${options?.expected ?? 'Expected'}:`).dim()}` + ); + + for (const line of splitExpected) + log(`${preIdentation} ${format(line).success().bold()}`); + + preIdentation = ''; + } + + if (options.throw) { + console.error(error); + hr(); + } + }, + onSkipFile({ message }) { + log(format(`◯ ${message}`).info().bold()); + }, + onSkipModifier({ message }) { + log( + `${indentation.hasDescribe ? ' ' : ''}${format(`◯ ${message}`).info().bold()}` + ); + }, + onTodoModifier({ message }) { + log( + `${indentation.hasDescribe ? ' ' : ''}${format(`● ${message}`).cyan().bold()}` + ); + }, + onFileResult({ status, path, duration, output }) { + stdout.write('\n'); + + if (status) { + log( + `${format('›').success().bold()} ${format(path.relative).success().underline()} ${format( + `› ${duration.toFixed(6)}ms` + ) + .success() + .dim()}` + ); + + if (output) log(output); + } else + log( + `${format('›').fail().bold()} ${format(path.relative).fail().underline()} ${format( + `› ${duration.toFixed(6)}ms` + ) + .fail() + .dim()}` + ); + + if (!status) + errors.push({ + file: path.relative, + output, + }); + }, + onRunResult() { + if (errors.length === 0) return; + + hr(); + log( + `${format(String(errors.length)).fail().bold()} ${format('test file(s) failed:').bold()}\n` + ); + + for (const i in errors) { + if (Object.prototype.hasOwnProperty.call(errors, i)) { + const { file, output } = errors[i]; + const index = +i; + + index > 0 && stdout.write('\n'); + + log( + `${format(`${index + 1})`) + .dim() + .bold()} ${format(file).underline()}` + ); + + output && log(`\n${output}`); + } + } + }, + onExit({ results, timespan }) { + const success = ` PASS › ${results.passed} `; + const failure = ` FAIL › ${results.failed} `; + const skips = ` SKIP › ${results.skipped} `; + const plans = ` TODO › ${results.todo} `; + const inline = results.skipped === 0 || results.todo === 0; + + let message = ''; + + if (inline) { + message += `${format(success).bg('green')} ${format(failure).bg(results.failed === 0 ? 'grey' : 'brightRed')}`; + + if (results.skipped) message += ` ${format(skips).bg('brightBlue')}`; + if (results.todo) message += ` ${format(plans).bg('brightBlue')}`; + } else { + message += `${format(success.trim()).success().bold()}\n`; + message += + results.failed === 0 + ? format(`${failure.trim()}\n`).bold() + : `${format(failure.trim()).fail().bold()}\n`; + message += `${format(skips.trim()).info().bold()}\n`; + message += `${format(plans.trim()).info().bold()}`; + } + + hr(); + log( + `${format(`Start at › ${format(`${parseTime(timespan.started)}`).bold()}`).dim()}` + ); + log( + `${format('Duration › ').dim()}${format( + `${timespan.duration.toFixed(6)}ms` + ) + .bold() + .dim()} ${format(`(±${parseTimeToSecs(timespan.duration)} seconds)`).dim()}` + ); + log( + `${format(`Files › ${format(String(results.passed + results.failed)).bold()}`).dim()}` + ); + log(`\n${message}\n`); + }, + }; +})(); diff --git a/src/services/reporters/verbose.ts b/src/services/reporters/verbose.ts new file mode 100644 index 00000000..69d0bf91 --- /dev/null +++ b/src/services/reporters/verbose.ts @@ -0,0 +1,18 @@ +import type { ReporterPlugin } from '../../@types/poku.js'; +import { createReporter } from '../../builders/reporter.js'; +import { format } from '../format.js'; +import { log } from '../write.js'; +import { poku } from './poku.js'; + +export const verbose: ReporterPlugin = (() => { + return createReporter({ + onRunStart() { + log(`${format('Running Tests').bold()}\n`); + }, + onFileResult({ status, path, duration, output }) { + poku.onFileResult({ status, path, duration, output }); + + if (!status && output) log(output); + }, + }); +})(); diff --git a/src/services/run-test-file.ts b/src/services/run-test-file.ts index 3a0c2e67..24626d30 100644 --- a/src/services/run-test-file.ts +++ b/src/services/run-test-file.ts @@ -1,30 +1,28 @@ import { hrtime, env } from 'node:process'; import { relative } from 'node:path'; import { spawn } from 'node:child_process'; -import { fileResults } from '../configs/files.js'; -import { isWindows, runner } from '../parsers/get-runner.js'; +import { runner } from '../parsers/get-runner.js'; import { parserOutput } from '../parsers/output.js'; import { beforeEach, afterEach } from './each.js'; -import { log } from './write.js'; import { deepOptions, GLOBAL, VERSION } from '../configs/poku.js'; +import { isWindows } from '../parsers/os.js'; -const { cwd } = GLOBAL; - -export const runTestFile = async (filePath: string): Promise => { - const runtimeOptions = runner(filePath); +export const runTestFile = async (path: string): Promise => { + const { cwd, configs, reporter } = GLOBAL; + const runtimeOptions = runner(path); const runtime = runtimeOptions.shift()!; const runtimeArguments = [ ...runtimeOptions, /* c8 ignore next 5 */ // Varies Platform - GLOBAL.configs.deno?.cjs === true || - (Array.isArray(GLOBAL.configs.deno?.cjs) && - GLOBAL.configs.deno.cjs.some((ext) => filePath.includes(ext))) + configs.deno?.cjs === true || + (Array.isArray(configs.deno?.cjs) && + configs.deno.cjs.some((ext) => path.includes(ext))) ? `https://cdn.jsdelivr.net/npm/poku${VERSION ? `@${VERSION}` : ''}/lib/polyfills/deno.mjs` - : filePath, + : path, ]; - const fileRelative = relative(cwd, filePath); - const showLogs = !GLOBAL.configs.quiet; + const file = relative(cwd, path); + const showLogs = !configs.quiet; let output = ''; @@ -35,7 +33,14 @@ export const runTestFile = async (filePath: string): Promise => { const start = hrtime(); let end: ReturnType; - if (!(await beforeEach(fileRelative))) return false; + if (!(await beforeEach(file))) return false; + + reporter.onFileStart({ + path: { + relative: file, + absolute: path, + }, + }); return new Promise((resolve) => { const child = spawn(runtime, [...runtimeArguments, ...deepOptions], { @@ -43,8 +48,9 @@ export const runTestFile = async (filePath: string): Promise => { shell: isWindows, env: { ...env, - POKU_FILE: fileRelative, + POKU_FILE: file, POKU_RUNTIME: env.POKU_RUNTIME, + POKU_REPORTER: configs.reporter, }, }); @@ -59,37 +65,51 @@ export const runTestFile = async (filePath: string): Promise => { const result = code === 0; if (showLogs) { - const mappedOutputs = parserOutput({ + const parsedOutputs = parserOutput({ output, result, + })?.join('\n'); + + const total = end[0] * 1e3 + end[1] / 1e6; + + reporter.onFileResult({ + status: result, + path: { + relative: file, + absolute: path, + }, + duration: total, + output: parsedOutputs, }); - - mappedOutputs && log(mappedOutputs.join('\n')); } - if (!(await afterEach(fileRelative))) { + if (!(await afterEach(file))) { resolve(false); return; } - const total = (end[0] * 1e3 + end[1] / 1e6).toFixed(6); - - if (result) fileResults.success.set(fileRelative, total); - else fileResults.fail.set(fileRelative, total); - resolve(result); }); - /* c8 ignore next 10 */ // Unknown external error + /* c8 ignore start */ // Unknown external error child.on('error', (err) => { end = hrtime(start); - const total = (end[0] * 1e3 + end[1] / 1e6).toFixed(6); + const total = end[0] * 1e3 + end[1] / 1e6; + + if (showLogs) console.error(`Failed to start test: ${path}`, err); - console.error(`Failed to start test: ${filePath}`, err); - fileResults.fail.set(fileRelative, total); + reporter.onFileResult({ + status: false, + path: { + relative: file, + absolute: path, + }, + duration: total, + }); resolve(false); }); + /* c8 ignore stop */ }); }; diff --git a/src/services/run-tests.ts b/src/services/run-tests.ts index ba76ec4b..413c0d1c 100644 --- a/src/services/run-tests.ts +++ b/src/services/run-tests.ts @@ -9,7 +9,6 @@ import { availableParallelism } from '../polyfills/os.js'; import { hasOnly } from '../parsers/get-arg.js'; const { cwd } = GLOBAL; -const failFastError = ` ${format('ℹ').fail()} ${format('failFast').bold()} is enabled`; if (hasOnly) deepOptions.push('--only'); @@ -19,13 +18,15 @@ export const runTests = async (dir: string): Promise => { let resolveDone: (value: boolean) => void; let rejectDone: (reason?: Error) => void; + const { configs } = GLOBAL; const testDir = join(cwd, dir); - const files = await listFiles(testDir, GLOBAL.configs); - const showLogs = !GLOBAL.configs.quiet; + const files = await listFiles(testDir, configs); + const showLogs = !configs.quiet; + const failFastError = ` ${format('ℹ').fail()} ${format('failFast').bold()} is enabled`; const concurrency: number = (() => { - if (GLOBAL.configs.sequential) return 1; + if (configs.sequential) return 1; const limit = - GLOBAL.configs.concurrency ?? Math.max(availableParallelism() - 1, 1); + configs.concurrency ?? Math.max(availableParallelism() - 1, 1); return limit <= 0 ? files.length || 1 : limit; })(); @@ -48,12 +49,12 @@ export const runTests = async (dir: string): Promise => { try { const testPassed = await runTestFile(filePath); - if (testPassed) ++results.success; + if (testPassed) ++results.passed; else { - ++results.fail; + ++results.failed; allPassed = false; - if (GLOBAL.configs.failFast) { + if (configs.failFast) { if (showLogs) { hr(); console.error(failFastError); diff --git a/test/__fixtures__/e2e/each-file/api.test.ts b/test/__fixtures__/e2e/each-file/api.test.ts new file mode 100644 index 00000000..d629304f --- /dev/null +++ b/test/__fixtures__/e2e/each-file/api.test.ts @@ -0,0 +1,226 @@ +import { test } from '../../../../src/modules/helpers/test.js'; +import { describe } from '../../../../src/modules/helpers/describe.js'; +import { it } from '../../../../src/modules/helpers/it/core.js'; +import { poku } from '../../../../src/modules/essentials/poku.js'; +import { assert } from '../../../../src/modules/essentials/assert.js'; + +test(async () => { + const prepareService = () => new Promise((resolve) => resolve(undefined)); + const resetService = () => new Promise((resolve) => resolve(undefined)); + const crashIt = () => new Promise((_, reject) => reject("Let's crash it")); + const crashItAgain = () => { + throw new Error("Let's crash it"); + }; + + await describe('Before and After Each: direct methods', async () => { + await it(async () => { + const code = await poku('../success', { + noExit: true, + beforeEach: prepareService, + afterEach: resetService, + }); + + assert.strictEqual( + code, + 0, + 'beforeEach and afterEach hooks with successful path' + ); + }); + + await it(async () => { + const code = await poku('../fail', { + noExit: true, + beforeEach: prepareService, + afterEach: resetService, + }); + + assert.strictEqual( + code, + 1, + 'beforeEach and afterEach hooks with failing path' + ); + }); + }); + + await describe('Before and After Each: called methods', async () => { + await it(async () => { + const code = await poku('../success', { + noExit: true, + beforeEach: () => prepareService(), + afterEach: () => resetService(), + }); + + assert.strictEqual( + code, + 0, + 'beforeEach and afterEach hooks with successful path' + ); + }); + + await it(async () => { + const code = await poku('../fail', { + noExit: true, + beforeEach: () => prepareService(), + afterEach: () => resetService(), + }); + + assert.strictEqual( + code, + 1, + 'beforeEach and afterEach hooks with failing path' + ); + }); + }); + + await describe('Before and After Each: await called methods', async () => { + await it(async () => { + const code = await poku('../success', { + noExit: true, + beforeEach: async () => await prepareService(), + afterEach: async () => await resetService(), + }); + + assert.strictEqual( + code, + 0, + 'beforeEach and afterEach hooks with successful path' + ); + }); + + await it(async () => { + const code = await poku('../fail', { + noExit: true, + beforeEach: async () => await prepareService(), + afterEach: async () => await resetService(), + }); + + assert.strictEqual( + code, + 1, + 'beforeEach and afterEach hooks with failing path' + ); + }); + }); + + await describe('Before and After Each: anonymous methods', async () => { + await it(async () => { + const code = await poku('../success', { + noExit: true, + beforeEach: () => true, + afterEach: () => true, + }); + + assert.strictEqual( + code, + 0, + 'beforeEach and afterEach hooks with successful path' + ); + }); + + await it(async () => { + const code = await poku('../fail', { + noExit: true, + beforeEach: () => true, + afterEach: () => true, + }); + + assert.strictEqual( + code, + 1, + 'beforeEach and afterEach hooks with failing path' + ); + }); + }); + + await describe('Before and After Each: anonymous methods (function)', async () => { + await it(async () => { + const code = await poku('../success', { + noExit: true, + beforeEach: function () { + return; + }, + afterEach: function () { + return; + }, + }); + + assert.strictEqual( + code, + 0, + 'beforeEach and afterEach hooks with successful path' + ); + }); + + await it(async () => { + const code = await poku('../fail', { + noExit: true, + beforeEach: function () { + return; + }, + afterEach: function () { + return; + }, + }); + + assert.strictEqual( + code, + 1, + 'beforeEach and afterEach hooks with failing path' + ); + }); + }); + + await describe('Before and After Each: Failure', async () => { + await it(async () => { + const code = await poku('../success', { + noExit: true, + beforeEach: crashIt, + afterEach: resetService, + }); + + assert.strictEqual( + code, + 1, + 'Rejects beforeEach hook with successful path' + ); + }); + + await it(async () => { + const code = await poku('../success', { + noExit: true, + beforeEach: prepareService, + afterEach: crashIt, + }); + + assert.strictEqual( + code, + 1, + 'Rejects afterEach hook with successful path' + ); + }); + + await it(async () => { + const code = await poku('../success', { + noExit: true, + beforeEach: crashItAgain, + afterEach: resetService, + }); + + assert.strictEqual( + code, + 1, + 'Throws beforeEach hook with successful path' + ); + }); + + await it(async () => { + const code = await poku('../success', { + noExit: true, + beforeEach: prepareService, + afterEach: crashItAgain, + }); + + assert.strictEqual(code, 1, 'Throws afterEach hook with successful path'); + }); + }); +}); diff --git a/test/__fixtures__/e2e/env/set-env.test.ts b/test/__fixtures__/e2e/env/set-env.test.ts index 550ccdfe..01232b64 100644 --- a/test/__fixtures__/e2e/env/set-env.test.ts +++ b/test/__fixtures__/e2e/env/set-env.test.ts @@ -1,8 +1,13 @@ import process from 'node:process'; import { test } from '../../../../src/modules/helpers/test.js'; import { assert } from '../../../../src/modules/essentials/assert.js'; +import { runtimeVersion } from '../../../../src/parsers/runtime-version.js'; +import { GLOBAL } from '../../../../src/configs/poku.js'; test('Defining Variables', () => { + const noValue = + GLOBAL.runtime === 'deno' && runtimeVersion >= 2 ? undefined : ''; + assert.strictEqual(process.env.HOST, '123.123.123.123', 'Basic'); assert.strictEqual(process.env.USER0, undefined, 'Comented Line'); assert.strictEqual(process.env.USER1, '#user', 'Using quoted #'); @@ -29,10 +34,10 @@ test('Defining Variables', () => { assert.strictEqual(process.env.PORT1, '8080', 'Using a number'); assert.strictEqual( process.env.PORT2, - '', + noValue, 'Valid env with full commented value' ); - assert.strictEqual(process.env.PORT3, '', 'Undefined local variable'); + assert.strictEqual(process.env.PORT3, noValue, 'Undefined local variable'); assert.strictEqual( process.env.WHO_AM_I, "I'm Poku", @@ -58,6 +63,10 @@ test('Defining Variables', () => { undefined, 'Undefined value (invalid)' ); - assert.strictEqual(process.env.NO_VALUE2, '', 'No value (valid)'); - assert.strictEqual(process.env.NO_VALUE3, '', 'No value (valid with spaces)'); + assert.strictEqual(process.env.NO_VALUE2, noValue, 'No value (valid)'); + assert.strictEqual( + process.env.NO_VALUE3, + noValue, + 'No value (valid with spaces)' + ); }); diff --git a/test/__fixtures__/e2e/list-files/a.cjs b/test/__fixtures__/e2e/list-files/a.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.cts b/test/__fixtures__/e2e/list-files/a.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.js b/test/__fixtures__/e2e/list-files/a.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.mjs b/test/__fixtures__/e2e/list-files/a.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.mts b/test/__fixtures__/e2e/list-files/a.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.spec.cjs b/test/__fixtures__/e2e/list-files/a.spec.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.spec.cts b/test/__fixtures__/e2e/list-files/a.spec.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.spec.js b/test/__fixtures__/e2e/list-files/a.spec.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.spec.mjs b/test/__fixtures__/e2e/list-files/a.spec.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.spec.mts b/test/__fixtures__/e2e/list-files/a.spec.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.spec.ts b/test/__fixtures__/e2e/list-files/a.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.test.cjs b/test/__fixtures__/e2e/list-files/a.test.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.test.cts b/test/__fixtures__/e2e/list-files/a.test.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.test.js b/test/__fixtures__/e2e/list-files/a.test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.test.mjs b/test/__fixtures__/e2e/list-files/a.test.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.test.mts b/test/__fixtures__/e2e/list-files/a.test.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.test.ts b/test/__fixtures__/e2e/list-files/a.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/a.ts b/test/__fixtures__/e2e/list-files/a.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.cjs b/test/__fixtures__/e2e/list-files/depth-1/a.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.cts b/test/__fixtures__/e2e/list-files/depth-1/a.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.js b/test/__fixtures__/e2e/list-files/depth-1/a.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.mjs b/test/__fixtures__/e2e/list-files/depth-1/a.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.mts b/test/__fixtures__/e2e/list-files/depth-1/a.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.spec.cjs b/test/__fixtures__/e2e/list-files/depth-1/a.spec.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.spec.cts b/test/__fixtures__/e2e/list-files/depth-1/a.spec.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.spec.js b/test/__fixtures__/e2e/list-files/depth-1/a.spec.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.spec.mjs b/test/__fixtures__/e2e/list-files/depth-1/a.spec.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.spec.mts b/test/__fixtures__/e2e/list-files/depth-1/a.spec.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.spec.ts b/test/__fixtures__/e2e/list-files/depth-1/a.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.test.cjs b/test/__fixtures__/e2e/list-files/depth-1/a.test.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.test.cts b/test/__fixtures__/e2e/list-files/depth-1/a.test.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.test.js b/test/__fixtures__/e2e/list-files/depth-1/a.test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.test.mjs b/test/__fixtures__/e2e/list-files/depth-1/a.test.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.test.mts b/test/__fixtures__/e2e/list-files/depth-1/a.test.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.test.ts b/test/__fixtures__/e2e/list-files/depth-1/a.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/a.ts b/test/__fixtures__/e2e/list-files/depth-1/a.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.cjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.cts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.js b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.mjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.mts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.cjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.cts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.js b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.mjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.mts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.ts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.cjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.cts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.js b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.mjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.mts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.ts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.ts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/a.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.cjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.cts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.js b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.mjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.mts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.cjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.cts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.js b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.mjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.mts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.ts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.cjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.cjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.cts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.cts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.js b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.mjs b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.mjs new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.mts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.mts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.ts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.ts b/test/__fixtures__/e2e/list-files/depth-1/depth-2/depth-3/a.ts new file mode 100644 index 00000000..e69de29b diff --git a/test/__fixtures__/e2e/reporters/failure/a.test.ts b/test/__fixtures__/e2e/reporters/failure/a.test.ts new file mode 100644 index 00000000..a6d67358 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/failure/a.test.ts @@ -0,0 +1,9 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; +import { describe } from '../../../../../src/modules/helpers/describe.js'; +import { it } from '../../../../../src/modules/helpers/it/core.js'; + +describe('Describing', () => { + it('Testing', () => { + assert(false, 'Fixture A'); + }); +}); diff --git a/test/__fixtures__/e2e/reporters/failure/b.test.ts b/test/__fixtures__/e2e/reporters/failure/b.test.ts new file mode 100644 index 00000000..9ffed878 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/failure/b.test.ts @@ -0,0 +1,3 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; + +assert(false, 'Fixture B'); diff --git a/test/__fixtures__/e2e/reporters/failure/c.test.ts b/test/__fixtures__/e2e/reporters/failure/c.test.ts new file mode 100644 index 00000000..200c7ab7 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/failure/c.test.ts @@ -0,0 +1,3 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; + +assert(false, 'Fixture C'); diff --git a/test/__fixtures__/e2e/reporters/failure/d.test.ts b/test/__fixtures__/e2e/reporters/failure/d.test.ts new file mode 100644 index 00000000..2afb1db0 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/failure/d.test.ts @@ -0,0 +1,3 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; + +assert(false, 'Fixture D'); diff --git a/test/__fixtures__/e2e/reporters/hybrid/a.test.ts b/test/__fixtures__/e2e/reporters/hybrid/a.test.ts new file mode 100644 index 00000000..57bc61a2 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/hybrid/a.test.ts @@ -0,0 +1,3 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; + +assert(true, 'Fixture A'); diff --git a/test/__fixtures__/e2e/reporters/hybrid/b.test.ts b/test/__fixtures__/e2e/reporters/hybrid/b.test.ts new file mode 100644 index 00000000..7f4c4844 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/hybrid/b.test.ts @@ -0,0 +1,3 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; + +assert(true, 'Fixture B'); diff --git a/test/__fixtures__/e2e/reporters/hybrid/c.test.ts b/test/__fixtures__/e2e/reporters/hybrid/c.test.ts new file mode 100644 index 00000000..200c7ab7 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/hybrid/c.test.ts @@ -0,0 +1,3 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; + +assert(false, 'Fixture C'); diff --git a/test/__fixtures__/e2e/reporters/hybrid/d.test.ts b/test/__fixtures__/e2e/reporters/hybrid/d.test.ts new file mode 100644 index 00000000..2afb1db0 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/hybrid/d.test.ts @@ -0,0 +1,3 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; + +assert(false, 'Fixture D'); diff --git a/test/__fixtures__/e2e/reporters/hybrid/e.test.ts b/test/__fixtures__/e2e/reporters/hybrid/e.test.ts new file mode 100644 index 00000000..716b4a86 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/hybrid/e.test.ts @@ -0,0 +1,3 @@ +import { skip } from '../../../../../src/modules/helpers/skip.js'; + +skip(); diff --git a/test/__fixtures__/e2e/reporters/hybrid/f.test.ts b/test/__fixtures__/e2e/reporters/hybrid/f.test.ts new file mode 100644 index 00000000..a2c55f03 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/hybrid/f.test.ts @@ -0,0 +1,8 @@ +import { describe } from '../../../../../src/modules/helpers/describe.js'; + +describe('Describing a Title'); + +describe.todo('Describing a Todo'); +// @ts-expect-error +describe.todo(); +describe.skip('Skipping a describe', () => {}); diff --git a/test/__fixtures__/e2e/reporters/success/a.test.ts b/test/__fixtures__/e2e/reporters/success/a.test.ts new file mode 100644 index 00000000..2da38f13 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/success/a.test.ts @@ -0,0 +1,9 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; +import { describe } from '../../../../../src/modules/helpers/describe.js'; +import { it } from '../../../../../src/modules/helpers/it/core.js'; + +describe('Describing', () => { + it('Testing', () => { + assert(true, 'Fixture A'); + }); +}); diff --git a/test/__fixtures__/e2e/reporters/success/b.test.ts b/test/__fixtures__/e2e/reporters/success/b.test.ts new file mode 100644 index 00000000..7f4c4844 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/success/b.test.ts @@ -0,0 +1,3 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; + +assert(true, 'Fixture B'); diff --git a/test/__fixtures__/e2e/reporters/success/c.test.ts b/test/__fixtures__/e2e/reporters/success/c.test.ts new file mode 100644 index 00000000..a8e7d437 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/success/c.test.ts @@ -0,0 +1,3 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; + +assert(true, 'Fixture C'); diff --git a/test/__fixtures__/e2e/reporters/success/d.test.ts b/test/__fixtures__/e2e/reporters/success/d.test.ts new file mode 100644 index 00000000..b6856546 --- /dev/null +++ b/test/__fixtures__/e2e/reporters/success/d.test.ts @@ -0,0 +1,3 @@ +import { assert } from '../../../../../src/modules/essentials/assert.js'; + +assert(true, 'Fixture D'); diff --git a/test/__utils__/capture-cli.test.ts b/test/__utils__/capture-cli.test.ts index b0fe7dcc..f902d5c2 100644 --- a/test/__utils__/capture-cli.test.ts +++ b/test/__utils__/capture-cli.test.ts @@ -4,9 +4,10 @@ import { spawn, type SpawnOptionsWithoutStdio, } from 'node:child_process'; -import { isWindows, runner } from '../../src/parsers/get-runner.js'; +import { runner } from '../../src/parsers/get-runner.js'; import { kill as pokuKill } from '../../src/modules/helpers/kill.js'; -import { getRuntime } from '../../src/parsers/get-runtime.js'; +import { isWindows } from '../../src/parsers/os.js'; +import { GLOBAL } from '../../src/configs/poku.js'; export const isBuild = process.env.NODE_ENV === 'build'; @@ -107,7 +108,7 @@ export const watchCLI = ( command: string, options?: SpawnOptionsWithoutStdio ): WatchCLIResult => { - const runtime = getRuntime(); + const { runtime } = GLOBAL; const binFile = 'src/bin/index.ts'; const basePath = typeof options?.cwd === 'string' diff --git a/test/c8.test.ts b/test/c8.test.ts index 31ea8ef7..3698fb92 100644 --- a/test/c8.test.ts +++ b/test/c8.test.ts @@ -1,6 +1,6 @@ import { rmSync } from 'node:fs'; import { poku, test, describe, it, assert } from '../src/modules/index.js'; -import { isWindows } from '../src/parsers/get-runner.js'; +import { isWindows } from '../src/parsers/os.js'; import { inspectPoku } from './__utils__/capture-cli.test.js'; import { GLOBAL } from '../src/configs/poku.js'; import { beforeEach } from '../src/modules/helpers/each.js'; diff --git a/test/e2e/background-process.test.ts b/test/e2e/background-process.test.ts index eae01be0..9e6959e2 100644 --- a/test/e2e/background-process.test.ts +++ b/test/e2e/background-process.test.ts @@ -8,11 +8,11 @@ import { } from '../../src/modules/helpers/create-service.js'; import { legacyFetch } from '../__utils__/legacy-fetch.test.js'; import { ext } from '../__utils__/capture-cli.test.js'; -import { getRuntime } from '../../src/parsers/get-runtime.js'; import { waitForPort } from '../../src/modules/helpers/wait-for.js'; +import { GLOBAL } from '../../src/configs/poku.js'; test(async () => { - const runtime = getRuntime(); + const { runtime } = GLOBAL; await describe('Start Service (Single Port)', async () => { await it(async () => { diff --git a/test/e2e/before-and-after-each.test.ts b/test/e2e/before-and-after-each.test.ts index b5bb794e..aff02e0e 100644 --- a/test/e2e/before-and-after-each.test.ts +++ b/test/e2e/before-and-after-each.test.ts @@ -1,230 +1,43 @@ -import { test } from '../../src/modules/helpers/test.js'; import { describe } from '../../src/modules/helpers/describe.js'; import { it } from '../../src/modules/helpers/it/core.js'; -import { poku } from '../../src/modules/essentials/poku.js'; import { assert } from '../../src/modules/essentials/assert.js'; -import { getRuntime } from '../../src/parsers/get-runtime.js'; -import { skip } from '../../src/modules/helpers/skip.js'; - -if (getRuntime() === 'deno') skip(); - -test(async () => { - const prepareService = () => new Promise((resolve) => resolve(undefined)); - const resetService = () => new Promise((resolve) => resolve(undefined)); - const crashIt = () => new Promise((_, reject) => reject("Let's crash it")); - const crashItAgain = () => { - throw new Error("Let's crash it"); - }; - - await describe('Before and After Each: direct methods', async () => { - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/success', { - noExit: true, - beforeEach: prepareService, - afterEach: resetService, - }); - - assert.strictEqual( - code, - 0, - 'beforeEach and afterEach hooks with successful path' - ); - }); - - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/fail', { - noExit: true, - beforeEach: prepareService, - afterEach: resetService, - }); - - assert.strictEqual( - code, - 1, - 'beforeEach and afterEach hooks with failing path' - ); - }); - }); - - await describe('Before and After Each: called methods', async () => { - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/success', { - noExit: true, - beforeEach: () => prepareService(), - afterEach: () => resetService(), - }); - - assert.strictEqual( - code, - 0, - 'beforeEach and afterEach hooks with successful path' - ); - }); - - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/fail', { - noExit: true, - beforeEach: () => prepareService(), - afterEach: () => resetService(), - }); - - assert.strictEqual( - code, - 1, - 'beforeEach and afterEach hooks with failing path' - ); - }); - }); - - await describe('Before and After Each: await called methods', async () => { - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/success', { - noExit: true, - beforeEach: async () => await prepareService(), - afterEach: async () => await resetService(), - }); - - assert.strictEqual( - code, - 0, - 'beforeEach and afterEach hooks with successful path' - ); - }); - - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/fail', { - noExit: true, - beforeEach: async () => await prepareService(), - afterEach: async () => await resetService(), - }); - - assert.strictEqual( - code, - 1, - 'beforeEach and afterEach hooks with failing path' - ); - }); - }); - - await describe('Before and After Each: anonymous methods', async () => { - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/success', { - noExit: true, - beforeEach: () => true, - afterEach: () => true, - }); - - assert.strictEqual( - code, - 0, - 'beforeEach and afterEach hooks with successful path' - ); - }); - - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/fail', { - noExit: true, - beforeEach: () => true, - afterEach: () => true, - }); - - assert.strictEqual( - code, - 1, - 'beforeEach and afterEach hooks with failing path' - ); - }); - }); - - await describe('Before and After Each: anonymous methods (function)', async () => { - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/success', { - noExit: true, - beforeEach: function () { - return; - }, - afterEach: function () { - return; - }, - }); - - assert.strictEqual( - code, - 0, - 'beforeEach and afterEach hooks with successful path' - ); - }); - - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/fail', { - noExit: true, - beforeEach: function () { - return; - }, - afterEach: function () { - return; - }, - }); - - assert.strictEqual( - code, - 1, - 'beforeEach and afterEach hooks with failing path' - ); - }); - }); - - await describe('Before and After Each: Failure', async () => { - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/success', { - noExit: true, - beforeEach: crashIt, - afterEach: resetService, - }); - - assert.strictEqual( - code, - 1, - 'Rejects beforeEach hook with successful path' - ); - }); - - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/success', { - noExit: true, - beforeEach: prepareService, - afterEach: crashIt, - }); - - assert.strictEqual( - code, - 1, - 'Rejects afterEach hook with successful path' - ); - }); - - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/success', { - noExit: true, - beforeEach: crashItAgain, - afterEach: resetService, - }); - - assert.strictEqual( - code, - 1, - 'Throws beforeEach hook with successful path' - ); - }); - - await it(async () => { - const code = await poku('./test/__fixtures__/e2e/success', { - noExit: true, - beforeEach: prepareService, - afterEach: crashItAgain, - }); - - assert.strictEqual(code, 1, 'Throws afterEach hook with successful path'); - }); +import { inspectPoku, ext } from '../__utils__/capture-cli.test.js'; + +describe(async () => { + await it('Before and After Each File', async () => { + const output = await inspectPoku(`-d api.test.${ext}`, { + cwd: 'test/__fixtures__/e2e/each-file', + }); + + if (output.exitCode !== 0) { + console.log(output.stdout); + console.log(output.stderr); + } + + assert.strictEqual(output.exitCode, 0, 'Should Pass'); + + assert.match( + output.stdout, + /● Before and After Each: direct methods/, + 'Before and After Each: direct methods' + ); + + assert.match( + output.stdout, + /● Before and After Each: called methods/, + 'Before and After Each: called methods' + ); + + assert.match( + output.stdout, + /● Before and After Each: await called methods/, + 'Before and After Each: await called methods' + ); + + assert.match( + output.stdout, + /● Before and After Each: Failure/, + 'Before and After Each: Failure' + ); }); }); diff --git a/test/e2e/cli-ensure-flags.test.ts b/test/e2e/cli-ensure-flags.test.ts index 0c56ae54..64981a22 100644 --- a/test/e2e/cli-ensure-flags.test.ts +++ b/test/e2e/cli-ensure-flags.test.ts @@ -2,14 +2,10 @@ import { describe } from '../../src/modules/helpers/describe.js'; import { it } from '../../src/modules/helpers/it/core.js'; import { assert } from '../../src/modules/essentials/assert.js'; import { inspectPoku, isBuild } from '../__utils__/capture-cli.test.js'; -import { getRuntime } from '../../src/parsers/get-runtime.js'; import { skip } from '../../src/modules/helpers/skip.js'; +import { GLOBAL } from '../../src/configs/poku.js'; -const runtime = getRuntime(); - -if (isBuild || runtime === 'deno') { - skip(); -} +if (isBuild || GLOBAL.runtime !== 'node') skip(); describe('Enforce Option', async () => { await it('--enforce', async () => { diff --git a/test/e2e/cli-flags.test.ts b/test/e2e/cli-flags.test.ts index ce1fcc6d..6f94db20 100644 --- a/test/e2e/cli-flags.test.ts +++ b/test/e2e/cli-flags.test.ts @@ -4,9 +4,7 @@ import { assert } from '../../src/modules/essentials/assert.js'; import { inspectPoku, isBuild } from '../__utils__/capture-cli.test.js'; import { skip } from '../../src/modules/helpers/skip.js'; -if (isBuild) { - skip(); -} +if (isBuild) skip(); describe('CLI Flags', async () => { await it('Short flags', async () => { diff --git a/test/e2e/config-files.test.ts b/test/e2e/config-files.test.ts index 2ea41a49..c4f38b0f 100644 --- a/test/e2e/config-files.test.ts +++ b/test/e2e/config-files.test.ts @@ -2,12 +2,10 @@ import { describe } from '../../src/modules/helpers/describe.js'; import { it } from '../../src/modules/helpers/it/core.js'; import { assert } from '../../src/modules/essentials/assert.js'; import { inspectPoku, isBuild } from '../__utils__/capture-cli.test.js'; -import { getRuntime } from '../../src/parsers/get-runtime.js'; import { skip } from '../../src/modules/helpers/skip.js'; +import { GLOBAL } from '../../src/configs/poku.js'; -if (isBuild) { - skip(); -} +if (isBuild) skip(); describe('Test Runtimes/Platforms + Extensions', async () => { await it('.pokurc.jsonc', async () => { @@ -40,9 +38,7 @@ describe('Test Runtimes/Platforms + Extensions', async () => { assert(/debug/.test(output.stdout), 'CLI needs to pass able "debug"'); }); - if (getRuntime() === 'deno') { - return; - } + if (GLOBAL.runtime === 'deno') return; await it('poku.config.js', async () => { const output = await inspectPoku('', { @@ -98,6 +94,8 @@ describe('Test Runtimes/Platforms + Extensions', async () => { }); await it('Wrong Props', async () => { + if (GLOBAL.runtime === 'bun') return; + const output = await inspectPoku('-x', { cwd: 'test/__fixtures__/e2e/config-files/wrong-props', }); diff --git a/test/e2e/each-api-order.test.ts b/test/e2e/each-api-order.test.ts index 5d8bf1d5..bbb38ea8 100644 --- a/test/e2e/each-api-order.test.ts +++ b/test/e2e/each-api-order.test.ts @@ -1,11 +1,11 @@ -import { getRuntime } from '../../src/parsers/get-runtime.js'; import { describe } from '../../src/modules/helpers/describe.js'; import { it } from '../../src/modules/helpers/it/core.js'; import { assert } from '../../src/modules/essentials/assert.js'; import { ext, inspectPoku } from '../__utils__/capture-cli.test.js'; import { skip } from '../../src/modules/helpers/skip.js'; +import { GLOBAL } from '../../src/configs/poku.js'; -if (getRuntime() === 'deno') skip(); +if (GLOBAL.runtime === 'deno') skip(); const output = [ { diff --git a/test/e2e/env-file.test.ts b/test/e2e/env-file.test.ts index 0bd734b8..84a0bb34 100644 --- a/test/e2e/env-file.test.ts +++ b/test/e2e/env-file.test.ts @@ -3,12 +3,10 @@ import { describe } from '../../src/modules/helpers/describe.js'; import { it } from '../../src/modules/helpers/it/core.js'; import { assert } from '../../src/modules/essentials/assert.js'; import { inspectPoku, isBuild } from '../__utils__/capture-cli.test.js'; -import { isWindows } from '../../src/parsers/get-runner.js'; +import { isWindows } from '../../src/parsers/os.js'; import { skip } from '../../src/modules/helpers/skip.js'; -if (isBuild) { - skip(); -} +if (isBuild) skip(); describe('.env File', async () => { await it('CLI Env Variables Propagation (default)', async () => { diff --git a/test/e2e/fail-fast.test.ts b/test/e2e/fail-fast.test.ts index 36f584fd..d9ab714e 100644 --- a/test/e2e/fail-fast.test.ts +++ b/test/e2e/fail-fast.test.ts @@ -2,9 +2,9 @@ import { describe } from '../../src/modules/helpers/describe.js'; import { assert } from '../../src/modules/essentials/assert.js'; import { inspectPoku, isBuild } from '../__utils__/capture-cli.test.js'; import { skip } from '../../src/modules/helpers/skip.js'; -import { getRuntime } from '../../src/parsers/get-runtime.js'; +import { GLOBAL } from '../../src/configs/poku.js'; -if (isBuild || getRuntime() === 'deno') skip(); +if (isBuild || GLOBAL.runtime === 'deno') skip(); describe('Fail Fast', async () => { const results = await inspectPoku('', { diff --git a/test/e2e/failure.test.ts b/test/e2e/failure.test.ts index d41a3b87..93d4c8a6 100644 --- a/test/e2e/failure.test.ts +++ b/test/e2e/failure.test.ts @@ -1,11 +1,13 @@ -import { getRuntime } from '../../src/parsers/get-runtime.js'; import { describe } from '../../src/modules/helpers/describe.js'; import { it } from '../../src/modules/helpers/it/core.js'; import { assert } from '../../src/modules/essentials/assert.js'; import { inspectPoku, isBuild } from '../__utils__/capture-cli.test.js'; import { skip } from '../../src/modules/helpers/skip.js'; +import { GLOBAL } from '../../src/configs/poku.js'; -if (getRuntime() === 'deno') skip(); +const { runtime } = GLOBAL; + +if (runtime === 'deno') skip(); describe('Failure', async () => { await it('Basic', async () => { @@ -32,7 +34,7 @@ describe('Failure', async () => { ); }); - getRuntime() === 'node' && + runtime === 'node' && !isBuild && (await it('Wrong Sintax', async () => { const results = await inspectPoku('invalid-file.js', { diff --git a/test/e2e/help.test.ts b/test/e2e/help.test.ts index 6bf5d2e7..c6efe225 100644 --- a/test/e2e/help.test.ts +++ b/test/e2e/help.test.ts @@ -3,11 +3,9 @@ import { it } from '../../src/modules/helpers/it/core.js'; import { skip } from '../../src/modules/helpers/skip.js'; import { assert } from '../../src/modules/essentials/assert.js'; import { inspectPoku, isBuild } from '../__utils__/capture-cli.test.js'; -import { getRuntime } from '../../src/parsers/get-runtime.js'; +import { GLOBAL } from '../../src/configs/poku.js'; -const runtime = getRuntime(); - -if (isBuild || runtime === 'deno') skip(); +if (isBuild || GLOBAL.runtime === 'deno') skip(); describe('Help', async () => { await it('--help', async () => { diff --git a/test/e2e/ignored-paths.test.ts b/test/e2e/ignored-paths.test.ts index e7994fb3..a72b6b27 100644 --- a/test/e2e/ignored-paths.test.ts +++ b/test/e2e/ignored-paths.test.ts @@ -3,7 +3,7 @@ import { accessSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; import { describe } from '../../src/modules/helpers/describe.js'; import { assert } from '../../src/modules/essentials/assert.js'; import { inspectPoku } from '../__utils__/capture-cli.test.js'; -import { isWindows } from '../../src/parsers/get-runner.js'; +import { isWindows } from '../../src/parsers/os.js'; import { skip } from '../../src/modules/helpers/skip.js'; if (isWindows) skip(); diff --git a/test/e2e/list-files.test.ts b/test/e2e/list-files.test.ts new file mode 100644 index 00000000..0b582ff3 --- /dev/null +++ b/test/e2e/list-files.test.ts @@ -0,0 +1,163 @@ +import { describe } from '../../src/modules/helpers/describe.js'; +import { assert } from '../../src/modules/essentials/assert.js'; +import { inspectPoku, isBuild } from '../__utils__/capture-cli.test.js'; +import { skip } from '../../src/modules/helpers/skip.js'; +import { isWindows } from '../../src/parsers/os.js'; +import { it } from '../../src/modules/helpers/it/core.js'; +import { GLOBAL } from '../../src/configs/poku.js'; + +if (GLOBAL.runtime === 'deno' || isBuild || isWindows) skip(); + +describe('List Files command', async () => { + await it('Default', async () => { + const output = await inspectPoku('--listFiles', { + cwd: 'test/__fixtures__/e2e/list-files', + }); + + if (output.exitCode !== 0) { + console.log(output.stdout); + console.log(output.stderr); + } + + const actual = output.stdout.split('\n'); + const offset = 2; + + assert.strictEqual(output.exitCode, 0, 'Passed'); + + assert.match(actual[offset + 1], /-.+\sa\.spec\.cjs$/); + assert.match(actual[offset + 2], /-.+\sa\.spec\.cts$/); + assert.match(actual[offset + 3], /-.+\sa\.spec\.js$/); + assert.match(actual[offset + 4], /-.+\sa\.spec\.mjs$/); + assert.match(actual[offset + 5], /-.+\sa\.spec\.mts$/); + assert.match(actual[offset + 6], /-.+\sa\.spec\.ts$/); + + assert.match(actual[offset + 7], /-.+\sa\.test\.cjs$/); + assert.match(actual[offset + 8], /-.+\sa\.test\.cts$/); + assert.match(actual[offset + 9], /-.+\sa\.test\.js$/); + assert.match(actual[offset + 10], /-.+\sa\.test\.mjs$/); + assert.match(actual[offset + 11], /-.+\sa\.test\.mts$/); + assert.match(actual[offset + 12], /-.+\sa\.test\.ts$/); + + assert.match(actual[offset + 13], /-.+\sdepth-1\/a\.spec\.cjs$/); + assert.match(actual[offset + 14], /-.+\sdepth-1\/a\.spec\.cts$/); + assert.match(actual[offset + 15], /-.+\sdepth-1\/a\.spec\.js$/); + assert.match(actual[offset + 16], /-.+\sdepth-1\/a\.spec\.mjs$/); + assert.match(actual[offset + 17], /-.+\sdepth-1\/a\.spec\.mts$/); + assert.match(actual[offset + 18], /-.+\sdepth-1\/a\.spec\.ts$/); + + assert.match(actual[offset + 19], /-.+\sdepth-1\/a\.test\.cjs$/); + assert.match(actual[offset + 20], /-.+\sdepth-1\/a\.test\.cts$/); + assert.match(actual[offset + 21], /-.+\sdepth-1\/a\.test\.js$/); + assert.match(actual[offset + 22], /-.+\sdepth-1\/a\.test\.mjs$/); + assert.match(actual[offset + 23], /-.+\sdepth-1\/a\.test\.mts$/); + assert.match(actual[offset + 24], /-.+\sdepth-1\/a\.test\.ts$/); + + assert.match(actual[offset + 25], /-.+\sdepth-1\/depth-2\/a\.spec\.cjs$/); + assert.match(actual[offset + 26], /-.+\sdepth-1\/depth-2\/a\.spec\.cts$/); + assert.match(actual[offset + 27], /-.+\sdepth-1\/depth-2\/a\.spec\.js$/); + assert.match(actual[offset + 28], /-.+\sdepth-1\/depth-2\/a\.spec\.mjs$/); + assert.match(actual[offset + 29], /-.+\sdepth-1\/depth-2\/a\.spec\.mts$/); + assert.match(actual[offset + 30], /-.+\sdepth-1\/depth-2\/a\.spec\.ts$/); + + assert.match(actual[offset + 31], /-.+\sdepth-1\/depth-2\/a\.test\.cjs$/); + assert.match(actual[offset + 32], /-.+\sdepth-1\/depth-2\/a\.test\.cts$/); + assert.match(actual[offset + 33], /-.+\sdepth-1\/depth-2\/a\.test\.js$/); + assert.match(actual[offset + 34], /-.+\sdepth-1\/depth-2\/a\.test\.mjs$/); + assert.match(actual[offset + 35], /-.+\sdepth-1\/depth-2\/a\.test\.mts$/); + assert.match(actual[offset + 36], /-.+\sdepth-1\/depth-2\/a\.test\.ts$/); + + assert.match( + actual[offset + 37], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.spec\.cjs$/ + ); + assert.match( + actual[offset + 38], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.spec\.cts$/ + ); + assert.match( + actual[offset + 39], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.spec\.js$/ + ); + assert.match( + actual[offset + 40], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.spec\.mjs$/ + ); + assert.match( + actual[offset + 41], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.spec\.mts$/ + ); + assert.match( + actual[offset + 42], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.spec\.ts$/ + ); + + assert.match( + actual[offset + 43], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.test\.cjs$/ + ); + assert.match( + actual[offset + 44], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.test\.cts$/ + ); + assert.match( + actual[offset + 45], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.test\.js$/ + ); + assert.match( + actual[offset + 46], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.test\.mjs$/ + ); + assert.match( + actual[offset + 47], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.test\.mts$/ + ); + assert.match( + actual[offset + 48], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.test\.ts$/ + ); + }); + + await it('Default', async () => { + const output = await inspectPoku( + '--listFiles --filter=.spec. --exclude=ts', + { + cwd: 'test/__fixtures__/e2e/list-files', + } + ); + + if (output.exitCode !== 0) { + console.log(output.stdout); + console.log(output.stderr); + } + + const actual = output.stdout.split('\n'); + const offset = 2; + + assert.strictEqual(output.exitCode, 0, 'Passed'); + + assert.match(actual[offset + 1], /-.+\sa\.spec\.cjs$/); + assert.match(actual[offset + 2], /-.+\sa\.spec\.js$/); + assert.match(actual[offset + 3], /-.+\sa\.spec\.mjs$/); + + assert.match(actual[offset + 4], /-.+\sdepth-1\/a\.spec\.cjs$/); + assert.match(actual[offset + 5], /-.+\sdepth-1\/a\.spec\.js$/); + assert.match(actual[offset + 6], /-.+\sdepth-1\/a\.spec\.mjs$/); + + assert.match(actual[offset + 7], /-.+\sdepth-1\/depth-2\/a\.spec\.cjs$/); + assert.match(actual[offset + 8], /-.+\sdepth-1\/depth-2\/a\.spec\.js$/); + assert.match(actual[offset + 9], /-.+\sdepth-1\/depth-2\/a\.spec\.mjs$/); + + assert.match( + actual[offset + 10], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.spec\.cjs$/ + ); + assert.match( + actual[offset + 11], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.spec\.js$/ + ); + assert.match( + actual[offset + 12], + /-.+\sdepth-1\/depth-2\/depth-3\/a\.spec\.mjs$/ + ); + }); +}); diff --git a/test/e2e/no-tests-with-unlimited-concurrency.test.ts b/test/e2e/no-tests-with-unlimited-concurrency.test.ts index 60b566c6..09fd7212 100644 --- a/test/e2e/no-tests-with-unlimited-concurrency.test.ts +++ b/test/e2e/no-tests-with-unlimited-concurrency.test.ts @@ -3,9 +3,7 @@ import { assert } from '../../src/modules/essentials/assert.js'; import { inspectPoku, isBuild } from '../__utils__/capture-cli.test.js'; import { skip } from '../../src/modules/helpers/skip.js'; -if (isBuild) { - skip(); -} +if (isBuild) skip(); test('No tests with unlimited concurrency', async () => { const output = await inspectPoku('--debug --concurrency=0', { diff --git a/test/e2e/only.test.ts b/test/e2e/only.test.ts index 851f81d2..bd1db138 100644 --- a/test/e2e/only.test.ts +++ b/test/e2e/only.test.ts @@ -13,8 +13,7 @@ describe('Only', async () => { }); const actual = results.stdout.split('\n'); - const offset = - actual.findIndex((line) => line.includes('Running Tests')) + 1; + const offset = actual.findIndex((line) => line.includes('◌ 1')); if (results.exitCode !== 0) { console.log(results.stdout); @@ -23,43 +22,43 @@ describe('Only', async () => { assert.strictEqual(results.exitCode, 0, 'Passed'); - assert.match(actual[offset + 1], /1/); + assert.match(actual[offset + 0], /1/); + assert.match(actual[offset + 1], /3/); assert.match(actual[offset + 2], /3/); - assert.match(actual[offset + 3], /3/); - assert.match(actual[offset + 4], /1/); + assert.match(actual[offset + 3], /1/); + assert.match(actual[offset + 4], /4/); assert.match(actual[offset + 5], /4/); - assert.match(actual[offset + 6], /4/); + assert.match(actual[offset + 6], /5/); assert.match(actual[offset + 7], /5/); - assert.match(actual[offset + 8], /5/); - assert.match(actual[offset + 9], /8/); + assert.match(actual[offset + 8], /8/); + assert.match(actual[offset + 9], /9/); assert.match(actual[offset + 10], /9/); - assert.match(actual[offset + 11], /9/); + assert.match(actual[offset + 11], /10/); assert.match(actual[offset + 12], /10/); - assert.match(actual[offset + 13], /10/); + assert.match(actual[offset + 13], /11/); assert.match(actual[offset + 14], /11/); - assert.match(actual[offset + 15], /11/); - assert.match(actual[offset + 16], /8/); + assert.match(actual[offset + 15], /8/); - assert.match(actual[offset + 17], /15/); + assert.match(actual[offset + 16], /15/); + assert.match(actual[offset + 17], /17/); assert.match(actual[offset + 18], /17/); - assert.match(actual[offset + 19], /17/); - assert.match(actual[offset + 20], /15/); + assert.match(actual[offset + 19], /15/); - assert.match(actual[offset + 21], /19/); + assert.match(actual[offset + 20], /19/); + assert.match(actual[offset + 21], /20/); assert.match(actual[offset + 22], /20/); - assert.match(actual[offset + 23], /20/); + assert.match(actual[offset + 23], /21/); assert.match(actual[offset + 24], /21/); - assert.match(actual[offset + 25], /21/); + assert.match(actual[offset + 25], /22/); assert.match(actual[offset + 26], /22/); - assert.match(actual[offset + 27], /22/); - assert.match(actual[offset + 28], /19/); + assert.match(actual[offset + 27], /19/); - assert.match(actual[offset + 29], /23/); + assert.match(actual[offset + 28], /23/); + assert.match(actual[offset + 29], /25/); assert.match(actual[offset + 30], /25/); - assert.match(actual[offset + 31], /25/); - assert.match(actual[offset + 32], /23/); + assert.match(actual[offset + 31], /23/); }); await it('No Poku Runner', async () => { diff --git a/test/e2e/reporters.test.ts b/test/e2e/reporters.test.ts new file mode 100644 index 00000000..dc6e497c --- /dev/null +++ b/test/e2e/reporters.test.ts @@ -0,0 +1,135 @@ +import { describe } from '../../src/modules/helpers/describe.js'; +import { it } from '../../src/modules/helpers/it/core.js'; +import { assert } from '../../src/modules/essentials/assert.js'; +import { inspectPoku, isBuild } from '../__utils__/capture-cli.test.js'; +import { skip } from '../../src/modules/helpers/skip.js'; + +if (isBuild) skip(); + +describe('Reporters', async () => { + for (const flag of [ + '--reporter=poku', + '-r=poku', + '--reporter=verbose', + '-r=verbose', + '--reporter=classic', + '-r=classic', + '--reporter=dot', + '-r=dot', + ]) { + await it(`Hybrid: ${flag}`, async () => { + const output = await inspectPoku(flag, { + cwd: 'test/__fixtures__/e2e/reporters/hybrid', + }); + + if (output.exitCode !== 1) { + console.log(output.stdout); + console.log(output.stderr); + } + + assert.strictEqual(output.exitCode, 1, 'Failed'); + assert(/PASS › 4/.test(output.stdout), 'CLI needs to pass 4'); + assert(/FAIL › 2/.test(output.stdout), 'CLI needs to fail 2'); + }); + + await it(`Success: ${flag}`, async () => { + const output = await inspectPoku(flag, { + cwd: 'test/__fixtures__/e2e/reporters/success', + }); + + if (output.exitCode !== 0) { + console.log(output.stdout); + console.log(output.stderr); + } + + assert.strictEqual(output.exitCode, 0, 'Passed'); + assert(/PASS › 4/.test(output.stdout), 'CLI needs to pass 4'); + assert(/FAIL › 0/.test(output.stdout), 'CLI needs to fail 0'); + }); + + await it(`Failure: ${flag}`, async () => { + const output = await inspectPoku(flag, { + cwd: 'test/__fixtures__/e2e/reporters/failure', + }); + + if (output.exitCode !== 1) { + console.log(output.stdout); + console.log(output.stderr); + } + + assert.strictEqual(output.exitCode, 1, 'Failed'); + assert(/PASS › 0/.test(output.stdout), 'CLI needs to pass 0'); + assert(/FAIL › 4/.test(output.stdout), 'CLI needs to fail 4'); + }); + } + + for (const flag of [ + '--reporter=focus', + '-r=focus', + '--reporter=compact', + '-r=compact', + ]) { + await it(`Hybrid: ${flag}`, async () => { + const output = await inspectPoku(flag, { + cwd: 'test/__fixtures__/e2e/reporters/hybrid', + }); + + if (output.exitCode !== 1) { + console.log(output.stdout); + console.log(output.stderr); + } + + assert.strictEqual(output.exitCode, 1, 'Failed'); + assert( + /4.+test file\(s\) passed/.test(output.stdout), + 'CLI needs to pass 4' + ); + assert( + /2.+test file\(s\) failed/.test(output.stdout), + 'CLI needs to fail 2' + ); + }); + + await it(`Success: ${flag}`, async () => { + const output = await inspectPoku(flag, { + cwd: 'test/__fixtures__/e2e/reporters/success', + }); + + if (output.exitCode !== 0) { + console.log(output.stdout); + console.log(output.stderr); + } + + assert.strictEqual(output.exitCode, 0, 'Passed'); + assert( + /4.+test file\(s\) passed/.test(output.stdout), + 'CLI needs to pass 4' + ); + assert( + /0.+test file\(s\) failed/.test(output.stdout), + 'CLI needs to fail 0' + ); + }); + + await it(`Failure: ${flag}`, async () => { + const output = await inspectPoku(flag, { + cwd: 'test/__fixtures__/e2e/reporters/failure', + }); + + if (output.exitCode !== 1) { + console.log(output.stdout); + console.log(output.stderr); + } + + assert.strictEqual(output.exitCode, 1, 'Failed'); + assert( + /0.+test file\(s\) passed/.test(output.stdout), + 'CLI needs to pass 0' + ); + assert( + /4.+test file\(s\) failed/.test(output.stdout), + 'CLI needs to fail 4' + ); + }); + } +}); diff --git a/test/e2e/runners.test.ts b/test/e2e/runners.test.ts index f385a1bf..003a0680 100644 --- a/test/e2e/runners.test.ts +++ b/test/e2e/runners.test.ts @@ -5,9 +5,7 @@ import { assert } from '../../src/modules/essentials/assert.js'; import { inspectCLI, isBuild } from '../__utils__/capture-cli.test.js'; import { skip } from '../../src/modules/helpers/skip.js'; -if (isBuild) { - skip(); -} +if (isBuild) skip(); const hasNode = (() => { try { @@ -49,7 +47,7 @@ describe('Test Runtimes/Platforms + Extensions', async () => { } assert.strictEqual(output.exitCode, 0, 'Exit Code needs to be 0'); - assert(/PASS › 12/.test(output.stdout), 'CLI needs to pass 1'); + assert(/PASS › 12/.test(output.stdout), 'CLI needs to pass 12'); assert(/FAIL › 0/.test(output.stdout), 'CLI needs to fail 0'); })); @@ -65,7 +63,7 @@ describe('Test Runtimes/Platforms + Extensions', async () => { } assert.strictEqual(output.exitCode, 0, 'Exit Code needs to be 0'); - assert(/PASS › 12/.test(output.stdout), 'CLI needs to pass 1'); + assert(/PASS › 12/.test(output.stdout), 'CLI needs to pass 12'); assert(/FAIL › 0/.test(output.stdout), 'CLI needs to fail 0'); })); @@ -81,7 +79,7 @@ describe('Test Runtimes/Platforms + Extensions', async () => { } assert.strictEqual(output.exitCode, 0, 'Exit Code needs to be 0'); - assert(/PASS › 10/.test(output.stdout), 'CLI needs to pass 1'); + assert(/PASS › 10/.test(output.stdout), 'CLI needs to pass 10'); assert(/FAIL › 0/.test(output.stdout), 'CLI needs to fail 0'); })); }); diff --git a/test/e2e/sequential.test.ts b/test/e2e/sequential.test.ts index c98a5f68..5c16d799 100644 --- a/test/e2e/sequential.test.ts +++ b/test/e2e/sequential.test.ts @@ -1,10 +1,10 @@ -import { getRuntime } from '../../src/parsers/get-runtime.js'; import { describe } from '../../src/modules/helpers/describe.js'; import { assert } from '../../src/modules/essentials/assert.js'; import { inspectPoku, isBuild } from '../__utils__/capture-cli.test.js'; import { skip } from '../../src/modules/helpers/skip.js'; +import { GLOBAL } from '../../src/configs/poku.js'; -if (getRuntime() === 'deno' || isBuild) skip(); +if (GLOBAL.runtime === 'deno' || isBuild) skip(); describe('Ensure sequential runs', async () => { const results = await inspectPoku('--debug --sequential', { diff --git a/test/e2e/watch.test.ts b/test/e2e/watch.test.ts index 456c6f73..cd66a01a 100644 --- a/test/e2e/watch.test.ts +++ b/test/e2e/watch.test.ts @@ -1,11 +1,8 @@ -import { getRuntime } from '../../src/parsers/get-runtime.js'; import { isBuild, watchCLI } from '../__utils__/capture-cli.test.js'; -import { isWindows } from '../../src/parsers/get-runner.js'; +import { isWindows } from '../../src/parsers/os.js'; import { skip } from '../../src/modules/helpers/skip.js'; -if (isBuild || getRuntime() !== 'node' || isWindows) { - skip(); -} +if (isBuild || GLOBAL.runtime !== 'node' || isWindows) skip(); import { describe } from '../../src/modules/helpers/describe.js'; import { it } from '../../src/modules/helpers/it/core.js'; @@ -15,6 +12,7 @@ import { waitForExpectedResult, } from '../../src/modules/helpers/wait-for.js'; import { readFile, writeFile } from 'node:fs/promises'; +import { GLOBAL } from '../../src/configs/poku.js'; const saveFileUnchanged = async (filename: string) => { const data = await readFile(filename, 'utf8'); @@ -52,7 +50,7 @@ describe('Watch Mode', async () => { .split('\n') .filter((result) => /test\/a\.test\.ts/.test(result)).length; - assert(watched >= 2); + assert(watched >= 1); }); await it('Sub path', async () => { @@ -60,6 +58,6 @@ describe('Watch Mode', async () => { .split('\n') .filter((result) => /test\/sub\/b\.test\.ts/.test(result)).length; - assert(watched >= 2); + assert(watched >= 1); }); }); diff --git a/test/integration/before-and-after-each/external-file-update.test.ts b/test/integration/before-and-after-each/external-file-update.test.ts index 476e4c49..f305b2cb 100644 --- a/test/integration/before-and-after-each/external-file-update.test.ts +++ b/test/integration/before-and-after-each/external-file-update.test.ts @@ -3,10 +3,11 @@ import path from 'node:path'; import { test } from '../../../src/modules/helpers/test.js'; import { poku } from '../../../src/modules/essentials/poku.js'; import { assert } from '../../../src/modules/essentials/assert.js'; -import { getRuntime } from '../../../src/parsers/get-runtime.js'; import { skip } from '../../../src/modules/helpers/skip.js'; +import { GLOBAL } from '../../../src/configs/poku.js'; + +const { runtime } = GLOBAL; -const runtime = getRuntime(); if (runtime === 'deno') skip(); const testDir = path.resolve('test/__fixtures__/.temp'); diff --git a/test/integration/before-and-after-each/invalid.test.ts b/test/integration/before-and-after-each/invalid.test.ts index a8066ac0..5723d27f 100644 --- a/test/integration/before-and-after-each/invalid.test.ts +++ b/test/integration/before-and-after-each/invalid.test.ts @@ -2,10 +2,10 @@ import { test } from '../../../src/modules/helpers/test.js'; import { poku } from '../../../src/modules/essentials/poku.js'; import { ext } from '../../__utils__/capture-cli.test.js'; import { assert } from '../../../src/modules/essentials/assert.js'; -import { getRuntime } from '../../../src/parsers/get-runtime.js'; import { skip } from '../../../src/modules/helpers/skip.js'; +import { GLOBAL } from '../../../src/configs/poku.js'; -if (getRuntime() === 'deno') skip(); +if (GLOBAL.runtime === 'deno') skip(); test('Before and After Each: updating an external file', async () => { const prepareService = true; diff --git a/test/integration/containers/test-docker-compose.test.ts b/test/integration/containers/test-docker-compose.test.ts index 5955624c..15f3d481 100644 --- a/test/integration/containers/test-docker-compose.test.ts +++ b/test/integration/containers/test-docker-compose.test.ts @@ -5,7 +5,7 @@ import { it } from '../../../src/modules/helpers/it/core.js'; import { assert } from '../../../src/modules/essentials/assert.js'; import { docker } from '../../../src/modules/helpers/container.js'; import { legacyFetch } from '../../__utils__/legacy-fetch.test.js'; -import { isWindows } from '../../../src/parsers/get-runner.js'; +import { isWindows } from '../../../src/parsers/os.js'; import { waitForPort } from '../../../src/modules/helpers/wait-for.js'; import { kill } from '../../../src/modules/helpers/kill.js'; import { skip } from '../../../src/modules/helpers/skip.js'; diff --git a/test/integration/containers/test-dockerfile.test.ts b/test/integration/containers/test-dockerfile.test.ts index 6c016dda..66128b9c 100644 --- a/test/integration/containers/test-dockerfile.test.ts +++ b/test/integration/containers/test-dockerfile.test.ts @@ -6,7 +6,7 @@ import { assert } from '../../../src/modules/essentials/assert.js'; import { docker } from '../../../src/modules/helpers/container.js'; import { waitForPort } from '../../../src/modules/helpers/wait-for.js'; import { legacyFetch } from '../../__utils__/legacy-fetch.test.js'; -import { isWindows } from '../../../src/parsers/get-runner.js'; +import { isWindows } from '../../../src/parsers/os.js'; import { skip } from '../../../src/modules/helpers/skip.js'; import { kill } from '../../../src/modules/helpers/kill.js'; diff --git a/test/integration/import.test.ts b/test/integration/import.test.ts index 92f718be..150c86b4 100644 --- a/test/integration/import.test.ts +++ b/test/integration/import.test.ts @@ -1,8 +1,8 @@ import { test } from '../../src/modules/helpers/test.js'; -import { getRuntime } from '../../src/parsers/get-runtime.js'; import { skip } from '../../src/modules/helpers/skip.js'; +import { GLOBAL } from '../../src/configs/poku.js'; -if (getRuntime() === 'deno') skip(); +if (GLOBAL.runtime === 'deno') skip(); test(async () => { const index = await import('../../src/modules/index.js'); diff --git a/test/integration/strict/assert-no-message.test.ts b/test/integration/strict/assert-no-message.test.ts index d04b5504..e40e6130 100644 --- a/test/integration/strict/assert-no-message.test.ts +++ b/test/integration/strict/assert-no-message.test.ts @@ -1,14 +1,14 @@ import { skip } from '../../../src/modules/helpers/skip.js'; -import { getRuntime, nodeVersion } from '../../../src/parsers/get-runtime.js'; - -if (nodeVersion && nodeVersion < 16) { - skip('Strict method is available from Node.js 16'); -} - -if (getRuntime() === 'deno') skip(); - +import { runtimeVersion } from '../../../src/parsers/runtime-version.js'; import { describe } from '../../../src/modules/helpers/describe.js'; import { it } from '../../../src/modules/helpers/it/core.js'; +import { GLOBAL } from '../../../src/configs/poku.js'; + +const { runtime } = GLOBAL; + +if (runtime === 'deno') skip(); +if (runtime === 'node' && runtimeVersion < 16) + skip('Strict method is available from Node.js 16'); describe('Strict Suite (No Message)', async () => { const { strict: assert } = await import( diff --git a/test/integration/strict/assert.test.ts b/test/integration/strict/assert.test.ts index 8da7301a..acc3d6b2 100644 --- a/test/integration/strict/assert.test.ts +++ b/test/integration/strict/assert.test.ts @@ -1,11 +1,12 @@ import { describe } from '../../../src/modules/helpers/describe.js'; import { it } from '../../../src/modules/helpers/it/core.js'; -import { getRuntime, nodeVersion } from '../../../src/parsers/get-runtime.js'; import { skip } from '../../../src/modules/helpers/skip.js'; +import { runtimeVersion } from '../../../src/parsers/runtime-version.js'; +import { GLOBAL } from '../../../src/configs/poku.js'; -if ((nodeVersion && nodeVersion < 16) || getRuntime() === 'deno') { +if (GLOBAL.runtime === 'deno') skip(); +if (GLOBAL.runtime === 'node' && runtimeVersion < 16) skip('Strict method is available from Node.js 16'); -} describe('Strict Suite', async () => { const { strict: assert } = await import( diff --git a/test/integration/strict/ensure-strict.test.ts b/test/integration/strict/ensure-strict.test.ts index c427f404..2bef4baf 100644 --- a/test/integration/strict/ensure-strict.test.ts +++ b/test/integration/strict/ensure-strict.test.ts @@ -1,11 +1,11 @@ import { skip } from '../../../src/modules/helpers/skip.js'; -import { getRuntime, nodeVersion } from '../../../src/parsers/get-runtime.js'; import { describe } from '../../../src/modules/helpers/describe.js'; import { assert } from '../../../src/modules/essentials/assert.js'; +import { runtimeVersion } from '../../../src/parsers/runtime-version.js'; +import { GLOBAL } from '../../../src/configs/poku.js'; -if ((nodeVersion && nodeVersion < 16) || getRuntime() !== 'node') { - skip('Strict method is available from Node.js 16'); -} +if (GLOBAL.runtime !== 'node') skip(); +if (runtimeVersion < 16) skip('Strict method is available from Node.js 16'); describe('Ensure strict', async () => { const { strict } = await import('../../../src/modules/essentials/strict.js'); diff --git a/test/unit/define-configs.test.ts b/test/unit/define-configs.test.ts index 98a3888d..241b8c80 100644 --- a/test/unit/define-configs.test.ts +++ b/test/unit/define-configs.test.ts @@ -1,8 +1,8 @@ import { test } from '../../src/modules/helpers/test.js'; -import { getRuntime } from '../../src/parsers/get-runtime.js'; import { skip } from '../../src/modules/helpers/skip.js'; +import { GLOBAL } from '../../src/configs/poku.js'; -if (getRuntime() === 'deno') skip(); +if (GLOBAL.runtime === 'deno') skip(); test(async () => { const { assert } = await import('../../src/modules/essentials/assert.js'); diff --git a/test/unit/deno/allow.test.ts b/test/unit/deno/allow.test.ts index 86df7028..6eb8f122 100644 --- a/test/unit/deno/allow.test.ts +++ b/test/unit/deno/allow.test.ts @@ -1,11 +1,10 @@ import { test } from '../../../src/modules/helpers/test.js'; import { assert } from '../../../src/modules/essentials/assert.js'; import { runner } from '../../../src/parsers/get-runner.js'; -import { getRuntime } from '../../../src/parsers/get-runtime.js'; import { skip } from '../../../src/modules/helpers/skip.js'; import { GLOBAL } from '../../../src/configs/poku.js'; -const runtime = getRuntime(); +const { runtime } = GLOBAL; if (runtime !== 'deno') { skip('Skipping for non-Deno platforms'); diff --git a/test/unit/deno/cjs.test.ts b/test/unit/deno/cjs.test.ts index 9f1946cb..7fb1668e 100644 --- a/test/unit/deno/cjs.test.ts +++ b/test/unit/deno/cjs.test.ts @@ -3,13 +3,11 @@ import { spawn } from 'node:child_process'; import { test } from '../../../src/modules/helpers/test.js'; import { assert } from '../../../src/modules/essentials/assert.js'; import { skip } from '../../../src/modules/helpers/skip.js'; -import { getRuntime } from '../../../src/parsers/get-runtime.js'; +import { GLOBAL } from '../../../src/configs/poku.js'; -const runtime = getRuntime(); +const { runtime } = GLOBAL; -if (runtime !== 'deno') { - skip('Skipping for non-Deno platforms'); -} +if (runtime !== 'deno') skip('Skipping for non-Deno platforms'); test('Deno Compatibility', async () => { const POKU_FILE = 'test/__fixtures__/integration/deno/require.cjs'; diff --git a/test/unit/deno/deny.test.ts b/test/unit/deno/deny.test.ts index 9d979fb8..500516d9 100644 --- a/test/unit/deno/deny.test.ts +++ b/test/unit/deno/deny.test.ts @@ -1,15 +1,12 @@ import { test } from '../../../src/modules/helpers/test.js'; import { assert } from '../../../src/modules/essentials/assert.js'; import { runner } from '../../../src/parsers/get-runner.js'; -import { getRuntime } from '../../../src/parsers/get-runtime.js'; import { skip } from '../../../src/modules/helpers/skip.js'; import { GLOBAL } from '../../../src/configs/poku.js'; -const runtime = getRuntime(); +const { runtime } = GLOBAL; -if (runtime !== 'deno') { - skip('Skipping for non-Deno platforms'); -} +if (runtime !== 'deno') skip('Skipping for non-Deno platforms'); test('Deno Permissions (Deny)', () => { GLOBAL.configs = { diff --git a/test/unit/wait-for/wait-for-expected-result.test.ts b/test/unit/wait-for/wait-for-expected-result.test.ts index 910a7c70..97699f8f 100644 --- a/test/unit/wait-for/wait-for-expected-result.test.ts +++ b/test/unit/wait-for/wait-for-expected-result.test.ts @@ -1,10 +1,10 @@ import { test } from '../../../src/modules/helpers/test.js'; import { assert } from '../../../src/modules/essentials/assert.js'; import { waitForExpectedResult } from '../../../src/modules/helpers/wait-for.js'; -import { getRuntime } from '../../../src/parsers/get-runtime.js'; +import { GLOBAL } from '../../../src/configs/poku.js'; test('Wait For Expected Result', async () => { - const runtime = getRuntime(); + const { runtime } = GLOBAL; class SomeClass {} function someFunc() { diff --git a/test/unit/watch.test.ts b/test/unit/watch.test.ts index 06d7287a..e9ca0f94 100644 --- a/test/unit/watch.test.ts +++ b/test/unit/watch.test.ts @@ -4,38 +4,37 @@ import { it } from '../../src/modules/helpers/it/core.js'; import { describe } from '../../src/modules/helpers/describe.js'; import { beforeEach, afterEach } from '../../src/modules/helpers/each.js'; import { assert } from '../../src/modules/essentials/assert.js'; -import { getRuntime, nodeVersion } from '../../src/parsers/get-runtime.js'; import { watch } from '../../src/services/watch.js'; import { sleep } from '../../src/modules/helpers/wait-for.js'; import { skip } from '../../src/modules/helpers/skip.js'; import type { WatchCallback } from '../../src/@types/watch.js'; +import { runtimeVersion } from '../../src/parsers/runtime-version.js'; +import { GLOBAL } from '../../src/configs/poku.js'; -const runtime = getRuntime(); +const { runtime } = GLOBAL; -if (runtime === 'deno' || (nodeVersion && nodeVersion < 16)) { +if (runtime === 'deno') skip(); +if (runtime === 'node' && runtimeVersion < 16) skip('rmSync is available from Node.js 16 onwards'); -} const tmpDir = path.resolve('.', 'test/__fixtures__/.temp/watch'); const humanDelay = 750; const createTempDir = () => { - if (!fs.existsSync(tmpDir)) { - fs.mkdirSync(tmpDir, { recursive: true }); - } + if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir, { recursive: true }); fs.writeFileSync(path.join(tmpDir, 'file1.test.js'), 'export default {};'); fs.writeFileSync(path.join(tmpDir, 'file2.test.js'), 'export default {};'); }; const cleanTempDir = () => { - if (fs.existsSync(tmpDir)) { + if (fs.existsSync(tmpDir)) fs.rmSync(tmpDir, { recursive: true, force: true }); - } }; describe('Watcher Service', async () => { let callbackResults: { file: string; event: string }[] = []; + const callback: WatchCallback = (file, event) => { callbackResults.push({ file, event }); }; diff --git a/website/docs/documentation/poku/options/reporter.mdx b/website/docs/documentation/poku/options/reporter.mdx new file mode 100644 index 00000000..8ad69c98 --- /dev/null +++ b/website/docs/documentation/poku/options/reporter.mdx @@ -0,0 +1,274 @@ +--- +sidebar_position: 1 +tags: [reporter, reporters, dot, tap] +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# `reporter` + +Specify the reporter for test execution. + +## Reporters + +### `poku` _(default)_ + +- Logs all `describe`, `it`, `test` and `assert` titles and messages, including their modifiers. +- Logs only the file name for errors during the execution, then lists all logs for each failed file at the running end. + +### `dot` (inspired by [Mocha](https://github.com/mochajs/mocha)) + +- Logs a dot for each success file test and `F` for each failed file. +- Lists all logs for each failed file at the running end. + +### `compact` (partially inspired by modern [Tap](https://github.com/tapjs/tapjs)) + +- Lists only file paths with **`PASS`** or **`FAIL`** and, in case of failures, lists all logs for each failed file at the running end. + +### `focus` + +- Logs only errors (in real time). If there is no error, it just logs a small footnote resume. + +### `verbose` + +- Just like the default (`poku`), but logs errors both in real time and also at the running end. + +### `classic` + +- The standard report style from **version 2** to preserve our history 🐷 + +
+ +## Usage + +### CLI + + + + +```bash +npx poku --reporter=poku # default +``` + + + + +```bash +npx poku --reporter=dot +``` + + + + +```bash +npx poku --reporter=compact +``` + + + + +```bash +npx poku --reporter=focus +``` + + + + +```bash +npx poku --reporter=verbose +``` + + + + +```bash +npx poku --reporter=classic +``` + + + + +- Short flag: `-r`. + +### Config File + + + + +```ts +{ + // "$schema": "https://poku.io/schemas/configs.json", + "reporter": "poku" // default +} +``` + +```ts +const { defineConfig } = require('poku'); + +module.exports = defineConfig({ + reporter: 'poku', // default +}); +``` + + + + +```ts +{ + // "$schema": "https://poku.io/schemas/configs.json", + "reporter": "dot" +} +``` + +```ts +const { defineConfig } = require('poku'); + +module.exports = defineConfig({ + reporter: 'dot', +}); +``` + + + + +```ts +{ + // "$schema": "https://poku.io/schemas/configs.json", + "reporter": "compact" +} +``` + +```ts +const { defineConfig } = require('poku'); + +module.exports = defineConfig({ + reporter: 'compact', +}); +``` + + + + +```ts +{ + // "$schema": "https://poku.io/schemas/configs.json", + "reporter": "focus" +} +``` + +```ts +const { defineConfig } = require('poku'); + +module.exports = defineConfig({ + reporter: 'focus', +}); +``` + + + + +```ts +{ + // "$schema": "https://poku.io/schemas/configs.json", + "reporter": "verbose" +} +``` + +```ts +const { defineConfig } = require('poku'); + +module.exports = defineConfig({ + reporter: 'verbose', +}); +``` + + + + +```ts +{ + // "$schema": "https://poku.io/schemas/configs.json", + "reporter": "classic" +} +``` + +```ts +const { defineConfig } = require('poku'); + +module.exports = defineConfig({ + reporter: 'classic', +}); +``` + + + + +### API + + + + +```ts +await poku('./test', { + reporter: 'poku', // default +}); +``` + + + + +```ts +await poku('./test', { + reporter: 'dot', +}); +``` + + + + +```ts +await poku('./test', { + reporter: 'compact', +}); +``` + + + + +```ts +await poku('./test', { + reporter: 'focus', +}); +``` + + + + +```ts +await poku('./test', { + reporter: 'verbose', +}); +``` + + + + +```ts +await poku('./test', { + reporter: 'classic', +}); +``` + + + + +
+ +:::tip + +- To debug, see [`debug`](/docs/documentation/poku/options/debug) option. +- To quiet the output, see [`quiet`](/docs/documentation/poku/options/quiet) option. +- Both the `--debug` and `--quiet` options work with all reporters. + +::: diff --git a/website/docs/documentation/poku/options/sequential.mdx b/website/docs/documentation/poku/options/sequential.mdx index ae4fa923..4f2be7fb 100644 --- a/website/docs/documentation/poku/options/sequential.mdx +++ b/website/docs/documentation/poku/options/sequential.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 1 +sidebar_position: 1.1 tags: [runInBand, sequence, test-sequential, no-parallel, jobs] --- diff --git a/website/schemas/options.json b/website/schemas/options.json index c67e8ec4..570150a9 100644 --- a/website/schemas/options.json +++ b/website/schemas/options.json @@ -16,9 +16,9 @@ "description": "Specify the path(s) to include in the configuration. Doesn't support glob patterns.\nhttps://poku.io/docs/documentation/poku/include-files", "default": "." }, - "parallel": { + "sequential": { "type": "boolean", - "description": "Determines the mode of test execution.\nhttps://poku.io/docs/documentation/poku/options/parallel", + "description": "Run the tests files sequentially (alias to `concurrency=1`).\nhttps://poku.io/docs/documentation/poku/options/sequential", "default": false }, "filter": { @@ -92,11 +92,11 @@ "required": [], "additionalProperties": false }, - "platform": { + "reporter": { "type": "string", - "description": "Determines the platform for test execution.\nhttps://poku.io/docs/documentation/poku/options/platform", - "default": "node", - "enum": ["node", "bun", "deno"] + "description": "Specify the reporter for test execution.\nhttps://poku.io/docs/documentation/poku/options/reporter", + "default": "poku", + "enum": ["poku", "dot", "compact", "focus", "verbose", "classic"] }, "deno": { "type": "object", @@ -121,7 +121,7 @@ "items": { "type": "string" }, - "description": "CommonJS compatibility for Deno.\nhttps://poku.io/docs/documentation/poku/options/deno#cjs" + "description": "CommonJS compatibility for Deno v1.\nhttps://poku.io/docs/documentation/poku/options/deno#cjs" } }, "additionalProperties": false, diff --git a/website/src/css/_history.scss b/website/src/css/_history.scss index 6d5f9576..a8210e1c 100644 --- a/website/src/css/_history.scss +++ b/website/src/css/_history.scss @@ -1,3 +1,5 @@ +@use 'mixins' as *; + details { &.history { // Reset Docusaurus styles diff --git a/website/src/css/_personal.scss b/website/src/css/_personal.scss index 0f91e307..4e5a71c2 100644 --- a/website/src/css/_personal.scss +++ b/website/src/css/_personal.scss @@ -1,3 +1,5 @@ +@use 'mixins' as *; + header { @include flex(row, center, flex-start); gap: 15px; diff --git a/website/src/css/custom.scss b/website/src/css/custom.scss index 48786098..8882caca 100644 --- a/website/src/css/custom.scss +++ b/website/src/css/custom.scss @@ -1,5 +1,7 @@ +@use 'sass:meta' as *; +@use 'mixins' as *; + @import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,600..800;1,600..800&display=swap'); -@import 'mixins'; :root { --ifm-color-primary: #ff4e8c; @@ -236,8 +238,8 @@ html[data-theme='dark'] { } // Components -@import './loading'; -@import './faq'; -@import './personal'; -@import './history'; -@import './stability/main'; +@include load-css('loading'); +@include load-css('faq'); +@include load-css('personal'); +@include load-css('history'); +@include load-css('stability/main'); diff --git a/website/src/css/features.scss b/website/src/css/features.scss index 32fde353..a4741f06 100644 --- a/website/src/css/features.scss +++ b/website/src/css/features.scss @@ -1,4 +1,4 @@ -@import 'mixins'; +@use 'mixins' as *; .features { p, diff --git a/website/src/css/home.scss b/website/src/css/home.scss index 6d9a699a..01cfd76a 100644 --- a/website/src/css/home.scss +++ b/website/src/css/home.scss @@ -1,4 +1,4 @@ -@import 'mixins'; +@use 'mixins' as *; #__docusaurus { & > main#home { diff --git a/website/src/css/stability/_main.scss b/website/src/css/stability/_main.scss index 7b6db8ad..76d95e05 100644 --- a/website/src/css/stability/_main.scss +++ b/website/src/css/stability/_main.scss @@ -1,3 +1,6 @@ +@use 'sass:meta' as *; +@use '../mixins' as *; + section { &.stability { @include flex(column); @@ -51,4 +54,4 @@ section { } // Themes -@import 'dark'; +@include load-css('dark');