From 482b72fc5f02da87498a18e0ed844ecb6bc772ca Mon Sep 17 00:00:00 2001 From: Vladimir Date: Mon, 23 Jan 2023 10:14:50 +0100 Subject: [PATCH] feat!: move test runner into a separate package (#2721) Closes https://github.com/vitest-dev/vitest/issues/1470 --- .eslintignore | 2 + .eslintrc | 13 - .github/renovate.json5 | 2 - .tazerc.json | 3 +- bench/package.json | 11 +- bench/pnpm-lock.yaml | 1171 +++++++++++++++-- bench/src/bench.ts | 8 +- bench/test/vue/jest.config.js | 1 + bench/test/vue/setup.jest.js | 3 + docs/.vitepress/config.ts | 16 + docs/advanced/api.md | 67 + docs/advanced/runner.md | 148 +++ docs/config/index.md | 7 + docs/guide/ui.md | 2 +- package.json | 2 +- packages/browser/package.json | 1 + packages/browser/src/client/main.ts | 29 +- packages/browser/src/client/runner.ts | 41 + packages/browser/src/client/snapshot.ts | 26 + packages/browser/src/node/index.ts | 24 +- packages/browser/stubs/console.js | 1 - packages/browser/stubs/fs.js | 7 - packages/browser/stubs/local-pkg.js | 2 - packages/browser/stubs/module.js | 1 - packages/browser/stubs/noop.js | 5 - packages/browser/stubs/perf_hooks.js | 1 - packages/browser/stubs/tty.js | 3 - packages/coverage-c8/package.json | 2 +- packages/coverage-c8/src/provider.ts | 2 +- packages/coverage-istanbul/package.json | 2 +- packages/expect/package.json | 4 +- packages/expect/rollup.config.js | 1 + packages/expect/src/index.ts | 1 + packages/expect/src/jest-expect.ts | 23 +- packages/expect/src/jest-extend.ts | 6 +- packages/expect/src/jest-matcher-utils.ts | 165 ++- packages/expect/src/types.ts | 7 +- packages/runner/README.md | 5 + packages/runner/package.json | 45 + packages/runner/rollup.config.js | 57 + .../src/runtime => runner/src}/collect.ts | 41 +- .../src/runtime => runner/src}/context.ts | 36 +- .../src/runtime => runner/src}/hooks.ts | 8 +- packages/runner/src/index.ts | 5 + packages/runner/src/map.ts | 22 + packages/runner/src/run.ts | 339 +++++ packages/runner/src/setup.ts | 11 + .../src/runtime => runner/src}/suite.ts | 91 +- .../src/runtime => runner/src}/test-state.ts | 2 +- packages/runner/src/types/index.ts | 2 + packages/runner/src/types/runner.ts | 99 ++ packages/runner/src/types/tasks.ts | 232 ++++ .../src/runtime => runner/src/utils}/chain.ts | 0 .../{vitest => runner}/src/utils/collect.ts | 0 .../src/runtime => runner/src/utils}/error.ts | 39 +- packages/runner/src/utils/index.ts | 5 + packages/runner/src/utils/suite.ts | 22 + packages/runner/src/utils/tasks.ts | 39 + packages/runner/types.d.ts | 1 + packages/runner/utils.d.ts | 1 + .../client/components/views/ViewReport.cy.tsx | 3 + .../ui/client/components/views/ViewReport.vue | 2 +- packages/ui/cypress.config.ts | 9 + packages/ui/package.json | 4 +- packages/utils/diff.d.ts | 1 + packages/utils/helpers.d.ts | 1 + packages/utils/package.json | 1 + packages/utils/src/colors.ts | 46 + packages/utils/src/constants.ts | 2 + packages/utils/src/diff.ts | 16 +- packages/utils/src/display.ts | 46 + packages/utils/src/helpers.ts | 101 ++ packages/utils/src/index.ts | 6 +- packages/utils/src/random.ts | 21 + packages/utils/src/timers.ts | 35 + packages/vite-node/package.json | 2 +- packages/vite-node/src/client.ts | 2 + packages/vitest/LICENSE.md | 97 +- packages/vitest/browser.d.ts | 2 +- packages/vitest/package.json | 27 +- packages/vitest/rollup.config.js | 17 +- packages/vitest/runners.d.ts | 1 + packages/vitest/src/api/setup.ts | 22 +- packages/vitest/src/api/types.ts | 10 +- packages/vitest/src/browser.ts | 9 +- packages/vitest/src/index.ts | 16 +- .../vitest/src/integrations/chai/index.ts | 2 +- .../src/integrations/snapshot/client.ts | 8 +- .../vitest/src/integrations/snapshot/env.ts | 19 + .../snapshot/environments/node.ts | 28 + .../snapshot/port/inlineSnapshot.ts | 7 +- .../src/integrations/snapshot/port/state.ts | 38 +- .../src/integrations/snapshot/port/utils.ts | 27 +- packages/vitest/src/integrations/vi.ts | 2 +- packages/vitest/src/node/cli-api.ts | 2 +- packages/vitest/src/node/config.ts | 11 +- packages/vitest/src/node/core.ts | 11 +- packages/vitest/src/node/error.ts | 9 +- packages/vitest/src/node/pkg.ts | 36 + packages/vitest/src/node/plugins/index.ts | 3 +- packages/vitest/src/node/reporters/base.ts | 14 +- .../node/reporters/benchmark/table/index.ts | 2 +- .../reporters/benchmark/table/tableRender.ts | 12 +- packages/vitest/src/node/reporters/default.ts | 2 +- packages/vitest/src/node/reporters/dot.ts | 3 +- packages/vitest/src/node/reporters/junit.ts | 6 +- .../node/reporters/renderers/dotRenderer.ts | 2 +- .../node/reporters/renderers/listRenderer.ts | 8 +- .../src/node/reporters/renderers/utils.ts | 2 +- .../vitest/src/node/reporters/tap-flat.ts | 2 +- packages/vitest/src/node/reporters/tap.ts | 4 +- packages/vitest/src/node/reporters/utils.ts | 2 +- packages/vitest/src/node/reporters/verbose.ts | 2 +- .../src/node/sequencers/RandomSequencer.ts | 2 +- packages/vitest/src/node/state.ts | 16 +- packages/vitest/src/runners.ts | 2 + packages/vitest/src/runtime/benchmark.ts | 50 + packages/vitest/src/runtime/entry.ts | 64 +- packages/vitest/src/runtime/execute.ts | 10 +- packages/vitest/src/runtime/map.ts | 34 - packages/vitest/src/runtime/mocker.ts | 3 +- packages/vitest/src/runtime/rpc.ts | 7 +- packages/vitest/src/runtime/run.ts | 528 -------- .../vitest/src/runtime/runners/benchmark.ts | 139 ++ packages/vitest/src/runtime/runners/test.ts | 118 ++ packages/vitest/src/runtime/setup.common.ts | 23 + .../src/runtime/{setup.ts => setup.node.ts} | 56 +- packages/vitest/src/runtime/utils.ts | 15 - packages/vitest/src/runtime/worker.ts | 2 +- packages/vitest/src/suite.ts | 2 + packages/vitest/src/typecheck/collect.ts | 2 +- packages/vitest/src/typecheck/typechecker.ts | 3 +- packages/vitest/src/types/benchmark.ts | 18 +- packages/vitest/src/types/chai.ts | 9 +- packages/vitest/src/types/config.ts | 8 +- packages/vitest/src/types/general.ts | 23 +- packages/vitest/src/types/global.ts | 26 +- packages/vitest/src/types/tasks.ts | 258 +--- packages/vitest/src/types/worker.ts | 3 +- packages/vitest/src/utils/base.ts | 65 - packages/vitest/src/utils/diff.ts | 128 -- packages/vitest/src/utils/import.ts | 3 +- packages/vitest/src/utils/index.ts | 93 +- packages/vitest/src/utils/source-map.ts | 4 +- packages/vitest/src/utils/tasks.ts | 42 +- packages/vitest/src/utils/timers.ts | 14 +- packages/vitest/suite.d.ts | 1 + packages/web-worker/rollup.config.js | 2 +- packages/ws-client/package.json | 1 + pnpm-lock.yaml | 93 +- .../test/__snapshots__/snapshot.test.ts.snap | 3 + test/browser/test/snapshot.test.ts | 9 + test/core/package.json | 3 + .../test/__snapshots__/mocked.test.ts.snap | 28 +- test/core/test/chainable.test.ts | 2 +- test/core/test/diff.test.ts | 10 +- test/core/test/replace-matcher.test.ts | 2 +- test/core/test/serialize.test.ts | 2 +- test/core/test/utils.spec.ts | 3 +- test/core/vitest.config.ts | 1 - test/reporters/src/context.ts | 2 +- .../tests/__snapshots__/html.test.ts.snap | 8 +- .../__snapshots__/reporters.spec.ts.snap | 14 +- test/snapshots/tools/generate-inline-test.mjs | 8 +- test/vite-config/package.json | 2 +- tsconfig.json | 3 + 166 files changed, 3709 insertions(+), 1933 deletions(-) create mode 100644 bench/test/vue/setup.jest.js create mode 100644 docs/advanced/api.md create mode 100644 docs/advanced/runner.md create mode 100644 packages/browser/src/client/runner.ts create mode 100644 packages/browser/src/client/snapshot.ts delete mode 100644 packages/browser/stubs/console.js delete mode 100644 packages/browser/stubs/fs.js delete mode 100644 packages/browser/stubs/local-pkg.js delete mode 100644 packages/browser/stubs/module.js delete mode 100644 packages/browser/stubs/noop.js delete mode 100644 packages/browser/stubs/perf_hooks.js delete mode 100644 packages/browser/stubs/tty.js create mode 100644 packages/runner/README.md create mode 100644 packages/runner/package.json create mode 100644 packages/runner/rollup.config.js rename packages/{vitest/src/runtime => runner/src}/collect.ts (57%) rename packages/{vitest/src/runtime => runner/src}/context.ts (65%) rename packages/{vitest/src/runtime => runner/src}/hooks.ts (84%) create mode 100644 packages/runner/src/index.ts create mode 100644 packages/runner/src/map.ts create mode 100644 packages/runner/src/run.ts create mode 100644 packages/runner/src/setup.ts rename packages/{vitest/src/runtime => runner/src}/suite.ts (73%) rename packages/{vitest/src/runtime => runner/src}/test-state.ts (80%) create mode 100644 packages/runner/src/types/index.ts create mode 100644 packages/runner/src/types/runner.ts create mode 100644 packages/runner/src/types/tasks.ts rename packages/{vitest/src/runtime => runner/src/utils}/chain.ts (100%) rename packages/{vitest => runner}/src/utils/collect.ts (100%) rename packages/{vitest/src/runtime => runner/src/utils}/error.ts (87%) create mode 100644 packages/runner/src/utils/index.ts create mode 100644 packages/runner/src/utils/suite.ts create mode 100644 packages/runner/src/utils/tasks.ts create mode 100644 packages/runner/types.d.ts create mode 100644 packages/runner/utils.d.ts create mode 100644 packages/utils/diff.d.ts create mode 100644 packages/utils/helpers.d.ts create mode 100644 packages/utils/src/colors.ts create mode 100644 packages/utils/src/constants.ts create mode 100644 packages/utils/src/display.ts create mode 100644 packages/utils/src/random.ts create mode 100644 packages/utils/src/timers.ts create mode 100644 packages/vitest/runners.d.ts create mode 100644 packages/vitest/src/integrations/snapshot/env.ts create mode 100644 packages/vitest/src/integrations/snapshot/environments/node.ts create mode 100644 packages/vitest/src/node/pkg.ts create mode 100644 packages/vitest/src/runners.ts create mode 100644 packages/vitest/src/runtime/benchmark.ts delete mode 100644 packages/vitest/src/runtime/map.ts delete mode 100644 packages/vitest/src/runtime/run.ts create mode 100644 packages/vitest/src/runtime/runners/benchmark.ts create mode 100644 packages/vitest/src/runtime/runners/test.ts create mode 100644 packages/vitest/src/runtime/setup.common.ts rename packages/vitest/src/runtime/{setup.ts => setup.node.ts} (82%) delete mode 100644 packages/vitest/src/runtime/utils.ts create mode 100644 packages/vitest/src/suite.ts delete mode 100644 packages/vitest/src/utils/diff.ts create mode 100644 packages/vitest/suite.d.ts create mode 100644 test/browser/test/__snapshots__/snapshot.test.ts.snap create mode 100644 test/browser/test/snapshot.test.ts diff --git a/.eslintignore b/.eslintignore index c21fcfc1e06b..095125aecd40 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,6 @@ dist +html +bench.json node_modules *.svelte *.snap diff --git a/.eslintrc b/.eslintrc index ec395ff34572..ff85d08cece7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -21,19 +21,6 @@ } ] } - }, - { - "files": "packages/vitest/**/*.*", - "rules": { - "no-restricted-globals": [ - "error", - "setTimeout", - "clearTimeout", - "setInterval", - "clearInterval", - "performance" - ] - } } ] } diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 861f0fee557f..a2b7fb46871e 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -42,8 +42,6 @@ "@testing-library/user-event", // TODO: migrate "pretty-format", - // TODO: breaking changes - "pathe", // TODO: vite-plugin-pwa issue "esno", // user can install any version diff --git a/.tazerc.json b/.tazerc.json index 70f32d894238..d470b5832072 100644 --- a/.tazerc.json +++ b/.tazerc.json @@ -2,8 +2,7 @@ "exclude": [ "vue", "puppeteer", - "pretty-format", - "pathe" + "pretty-format" ], "packageMode": { "vue": "minor", diff --git a/bench/package.json b/bench/package.json index 396fffc382cc..8cdfabe0dd79 100644 --- a/bench/package.json +++ b/bench/package.json @@ -9,14 +9,23 @@ "@actions/core": "^1.10.0", "@actions/exec": "^1.1.1", "@actions/github": "^5.1.1", + "@babel/preset-env": "^7.18.2", + "@babel/preset-typescript": "^7.17.12", "@happy-dom/jest-environment": "^8.1.3", "@types/benchmark": "^2.1.2", + "@vitejs/plugin-vue": "^4.0.0", + "@vue/test-utils": "^2.2.7", + "@vue/vue3-jest": "^27.0.0", + "babel-jest": "^27.5.1", "benchmark": "^2.1.4", "esno": "^0.16.3", "execa": "^6.1.0", "fs-extra": "^11.1.0", + "jest": "^27.5.1", "markdown-table": "^3.0.3", "microtime": "^3.1.1", - "vitest": "link:../packages/vitest" + "ts-jest": "^27.1.5", + "vitest": "link:../packages/vitest", + "vue": "^3.2.45" } } diff --git a/bench/pnpm-lock.yaml b/bench/pnpm-lock.yaml index e51e222f9357..3ca596138bc0 100644 --- a/bench/pnpm-lock.yaml +++ b/bench/pnpm-lock.yaml @@ -7,32 +7,51 @@ importers: '@actions/core': ^1.10.0 '@actions/exec': ^1.1.1 '@actions/github': ^5.1.1 + '@babel/preset-env': ^7.18.2 + '@babel/preset-typescript': ^7.17.12 '@happy-dom/jest-environment': ^8.1.3 '@types/benchmark': ^2.1.2 + '@vitejs/plugin-vue': ^4.0.0 + '@vue/test-utils': ^2.2.7 + '@vue/vue3-jest': ^27.0.0 + babel-jest: ^27.5.1 benchmark: ^2.1.4 esno: ^0.16.3 execa: ^6.1.0 fs-extra: ^11.1.0 + jest: ^27.5.1 markdown-table: ^3.0.3 microtime: ^3.1.1 + ts-jest: ^27.1.5 vitest: link:../packages/vitest + vue: ^3.2.45 devDependencies: '@actions/core': 1.10.0 '@actions/exec': 1.1.1 '@actions/github': 5.1.1 + '@babel/preset-env': 7.18.2_@babel+core@7.20.5 + '@babel/preset-typescript': 7.17.12_@babel+core@7.20.5 '@happy-dom/jest-environment': 8.1.3 '@types/benchmark': 2.1.2 + '@vitejs/plugin-vue': 4.0.0_vite@4.0.0+vue@3.2.45 + '@vue/test-utils': 2.2.7_vue@3.2.45 + '@vue/vue3-jest': 27.0.0_rggaoj2gxgtz66f7k5b6ywrwv4 + babel-jest: 27.5.1_@babel+core@7.20.5 benchmark: 2.1.4 esno: 0.16.3 execa: 6.1.0 fs-extra: 11.1.0 + jest: 27.5.1 markdown-table: 3.0.3 microtime: 3.1.1 + ts-jest: 27.1.5_lymxezh3rizc3sf4nv2timwyqu vitest: link:../packages/vitest + vue: 3.2.45 ../packages/browser: specifiers: '@types/ws': ^8.5.4 + '@vitest/runner': workspace:* '@vitest/ws-client': workspace:* local-pkg: ^0.4.2 mlly: ^1.1.0 @@ -42,6 +61,7 @@ importers: sirv: ^2.0.2 vitest: workspace:* dependencies: + '@vitest/runner': link:../runner local-pkg: 0.4.2 mlly: 1.1.0 modern-node-polyfills: 0.0.9 @@ -56,14 +76,14 @@ importers: ../packages/coverage-c8: specifiers: c8: ^7.12.0 - pathe: ^0.2.0 + pathe: ^1.1.0 vite-node: workspace:* vitest: workspace:* dependencies: c8: 7.12.0 vitest: link:../vitest devDependencies: - pathe: 0.2.0 + pathe: 1.1.0 vite-node: link:../vite-node ../packages/coverage-istanbul: @@ -78,7 +98,7 @@ importers: istanbul-lib-report: ^3.0.0 istanbul-lib-source-maps: ^4.0.1 istanbul-reports: ^3.1.5 - pathe: ^0.2.0 + pathe: ^1.1.0 test-exclude: ^6.0.0 vitest: workspace:* dependencies: @@ -95,7 +115,7 @@ importers: '@types/istanbul-lib-report': 3.0.0 '@types/istanbul-lib-source-maps': 4.0.1 '@types/istanbul-reports': 3.0.1 - pathe: 0.2.0 + pathe: 1.1.0 ../packages/expect: specifiers: @@ -107,8 +127,19 @@ importers: '@vitest/spy': link:../spy '@vitest/utils': link:../utils chai: 4.3.7 + devDependencies: picocolors: 1.0.0 + ../packages/runner: + specifiers: + '@vitest/utils': workspace:* + p-limit: ^4.0.0 + pathe: ^1.1.0 + dependencies: + '@vitest/utils': link:../utils + p-limit: 4.0.0 + pathe: 1.1.0 + ../packages/spy: specifiers: tinyspy: ^1.0.2 @@ -126,6 +157,7 @@ importers: '@unocss/reset': ^0.48.3 '@vitejs/plugin-vue': ^4.0.0 '@vitejs/plugin-vue-jsx': ^3.0.0 + '@vitest/runner': workspace:* '@vitest/ws-client': workspace:* '@vueuse/core': ^9.10.0 ansi-to-html: ^0.7.2 @@ -138,6 +170,7 @@ importers: fast-glob: ^3.2.12 flatted: ^3.2.7 floating-vue: ^2.0.0-y.0 + pathe: ^1.1.0 picocolors: ^1.0.0 rollup: ^2.79.1 sirv: ^2.0.2 @@ -152,6 +185,8 @@ importers: dependencies: fast-glob: 3.2.12 flatted: 3.2.7 + pathe: 1.1.0 + picocolors: 1.0.0 sirv: 2.0.2 devDependencies: '@faker-js/faker': 7.6.0 @@ -163,6 +198,7 @@ importers: '@unocss/reset': 0.48.3 '@vitejs/plugin-vue': 4.0.0_vite@4.0.0+vue@3.2.45 '@vitejs/plugin-vue-jsx': 3.0.0_vite@4.0.0+vue@3.2.45 + '@vitest/runner': link:../runner '@vitest/ws-client': link:../ws-client '@vueuse/core': 9.10.0_vue@3.2.45 ansi-to-html: 0.7.2 @@ -173,7 +209,6 @@ importers: d3-graph-controller: 2.5.1 diff: 5.1.0 floating-vue: 2.0.0-y.0_vue@3.2.45 - picocolors: 1.0.0 rollup: 2.79.1 splitpanes: 3.1.5 unocss: 0.48.3_rollup@2.79.1+vite@4.0.0 @@ -189,11 +224,13 @@ importers: '@types/diff': ^5.0.2 cli-truncate: ^3.1.0 diff: ^5.1.0 + loupe: ^2.3.6 picocolors: ^1.0.0 pretty-format: ^27.5.1 dependencies: cli-truncate: 3.1.0 diff: 5.1.0 + loupe: 2.3.6 picocolors: 1.0.0 pretty-format: 27.5.1 devDependencies: @@ -207,7 +244,7 @@ importers: cac: ^6.7.14 debug: ^4.3.4 mlly: ^1.1.0 - pathe: ^0.2.0 + pathe: ^1.1.0 picocolors: ^1.0.0 rollup: ^2.79.1 source-map: ^0.6.1 @@ -217,7 +254,7 @@ importers: cac: 6.7.14 debug: 4.3.4 mlly: 1.1.0 - pathe: 0.2.0 + pathe: 1.1.0 picocolors: 1.0.0 source-map: 0.6.1 source-map-support: 0.5.21 @@ -244,6 +281,7 @@ importers: '@types/sinonjs__fake-timers': ^8.1.2 '@vitest/browser': '*' '@vitest/expect': workspace:* + '@vitest/runner': workspace:* '@vitest/spy': workspace:* '@vitest/ui': workspace:* '@vitest/utils': workspace:* @@ -272,13 +310,14 @@ importers: mlly: ^1.1.0 natural-compare: ^1.4.0 p-limit: ^4.0.0 - pathe: ^0.2.0 + pathe: ^1.1.0 picocolors: ^1.0.0 pkg-types: ^1.0.1 pretty-format: ^27.5.1 prompts: ^2.4.2 rollup: ^2.79.1 source-map: ^0.6.1 + std-env: ^3.3.1 strip-ansi: ^7.0.1 strip-literal: ^1.0.0 tinybench: ^2.3.1 @@ -287,26 +326,35 @@ importers: typescript: ^4.9.4 vite: ^3.0.0 || ^4.0.0 vite-node: workspace:* + why-is-node-running: ^2.2.2 ws: ^8.12.0 dependencies: '@types/chai': 4.3.4 '@types/chai-subset': 1.3.3 '@types/node': 17.0.35 '@vitest/browser': link:../browser + '@vitest/expect': link:../expect + '@vitest/runner': link:../runner + '@vitest/spy': link:../spy + '@vitest/ui': link:../ui + '@vitest/utils': link:../utils acorn: 8.8.1 acorn-walk: 8.2.0 cac: 6.7.14 chai: 4.3.7 debug: 4.3.4 local-pkg: 0.4.2 + pathe: 1.1.0 picocolors: 1.0.0 source-map: 0.6.1 + std-env: 3.3.1 strip-literal: 1.0.0 tinybench: 2.3.1 tinypool: 0.3.0 tinyspy: 1.0.2 vite: 3.2.5_@types+node@17.0.35 vite-node: link:../vite-node + why-is-node-running: 2.2.2 devDependencies: '@antfu/install-pkg': 0.1.1 '@edge-runtime/vm': 2.0.2 @@ -317,10 +365,6 @@ importers: '@types/natural-compare': 1.4.1 '@types/prompts': 2.4.2 '@types/sinonjs__fake-timers': 8.1.2 - '@vitest/expect': link:../expect - '@vitest/spy': link:../spy - '@vitest/ui': link:../ui - '@vitest/utils': link:../utils birpc: 0.2.3 chai-subset: 1.6.0 cli-truncate: 3.1.0 @@ -340,7 +384,6 @@ importers: mlly: 1.1.0 natural-compare: 1.4.0 p-limit: 4.0.0 - pathe: 0.2.0 pkg-types: 1.0.1 pretty-format: 27.5.1 prompts: 2.4.2 @@ -370,6 +413,7 @@ importers: ../packages/ws-client: specifiers: + '@vitest/runner': workspace:* birpc: ^0.2.3 flatted: ^3.2.7 rollup: ^2.79.1 @@ -379,6 +423,7 @@ importers: flatted: 3.2.7 ws: 8.12.0 devDependencies: + '@vitest/runner': link:../runner rollup: 2.79.1 test/vue: @@ -517,28 +562,6 @@ packages: - supports-color dev: true - /@babel/core/7.19.3: - resolution: {integrity: sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.20.5 - '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.19.3 - '@babel/helper-module-transforms': 7.20.2 - '@babel/helpers': 7.20.6 - '@babel/parser': 7.20.5 - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 - convert-source-map: 1.8.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.1 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - /@babel/core/7.20.5: resolution: {integrity: sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==} engines: {node: '>=6.9.0'} @@ -560,7 +583,6 @@ packages: semver: 6.3.0 transitivePeerDependencies: - supports-color - dev: true /@babel/generator/7.18.2: resolution: {integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==} @@ -627,38 +649,43 @@ packages: semver: 6.3.0 dev: true - /@babel/helper-compilation-targets/7.20.0_@babel+core@7.19.3: + /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.5: resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/compat-data': 7.20.5 - '@babel/core': 7.19.3 + '@babel/core': 7.20.5 '@babel/helper-validator-option': 7.18.6 browserslist: 4.21.4 semver: 6.3.0 - /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.5: - resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} + /@babel/helper-create-class-features-plugin/7.18.0_@babel+core@7.18.2: + resolution: {integrity: sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.20.5 - '@babel/core': 7.20.5 - '@babel/helper-validator-option': 7.18.6 - browserslist: 4.21.4 - semver: 6.3.0 + '@babel/core': 7.18.2 + '@babel/helper-annotate-as-pure': 7.16.7 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-member-expression-to-functions': 7.17.7 + '@babel/helper-optimise-call-expression': 7.16.7 + '@babel/helper-replace-supers': 7.16.7 + '@babel/helper-split-export-declaration': 7.18.6 + transitivePeerDependencies: + - supports-color dev: true - /@babel/helper-create-class-features-plugin/7.18.0_@babel+core@7.18.2: + /@babel/helper-create-class-features-plugin/7.18.0_@babel+core@7.20.5: resolution: {integrity: sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.20.5 '@babel/helper-annotate-as-pure': 7.16.7 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.19.0 @@ -717,6 +744,17 @@ packages: regexpu-core: 5.0.1 dev: true + /@babel/helper-create-regexp-features-plugin/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-annotate-as-pure': 7.18.6 + regexpu-core: 5.0.1 + dev: true + /@babel/helper-define-polyfill-provider/0.3.1_@babel+core@7.18.2: resolution: {integrity: sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==} peerDependencies: @@ -735,6 +773,24 @@ packages: - supports-color dev: true + /@babel/helper-define-polyfill-provider/0.3.1_@babel+core@7.20.5: + resolution: {integrity: sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==} + peerDependencies: + '@babel/core': ^7.4.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.5 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/traverse': 7.20.5 + debug: 4.3.4 + lodash.debounce: 4.0.8 + resolve: 1.22.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/helper-environment-visitor/7.18.9: resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} engines: {node: '>=6.9.0'} @@ -986,13 +1042,6 @@ packages: '@babel/types': 7.20.5 dev: true - /@babel/parser/7.19.3: - resolution: {integrity: sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.20.5 - /@babel/parser/7.20.5: resolution: {integrity: sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==} engines: {node: '>=6.0.0'} @@ -1010,6 +1059,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ==} engines: {node: '>=6.9.0'} @@ -1022,6 +1081,18 @@ packages: '@babel/plugin-proposal-optional-chaining': 7.17.12_@babel+core@7.18.2 dev: true + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 + '@babel/plugin-proposal-optional-chaining': 7.17.12_@babel+core@7.20.5 + dev: true + /@babel/plugin-proposal-async-generator-functions/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ==} engines: {node: '>=6.9.0'} @@ -1036,6 +1107,20 @@ packages: - supports-color dev: true + /@babel/plugin-proposal-async-generator-functions/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-remap-async-to-generator': 7.16.8 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.5 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-class-properties/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw==} engines: {node: '>=6.9.0'} @@ -1049,6 +1134,19 @@ packages: - supports-color dev: true + /@babel/plugin-proposal-class-properties/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-create-class-features-plugin': 7.20.5_@babel+core@7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-class-static-block/7.18.0_@babel+core@7.18.2: resolution: {integrity: sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA==} engines: {node: '>=6.9.0'} @@ -1063,6 +1161,20 @@ packages: - supports-color dev: true + /@babel/plugin-proposal-class-static-block/7.18.0_@babel+core@7.20.5: + resolution: {integrity: sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-create-class-features-plugin': 7.20.5_@babel+core@7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.5 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-dynamic-import/7.16.7_@babel+core@7.18.2: resolution: {integrity: sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==} engines: {node: '>=6.9.0'} @@ -1074,6 +1186,17 @@ packages: '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.18.2 dev: true + /@babel/plugin-proposal-dynamic-import/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.5 + dev: true + /@babel/plugin-proposal-export-namespace-from/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ==} engines: {node: '>=6.9.0'} @@ -1085,6 +1208,17 @@ packages: '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.18.2 dev: true + /@babel/plugin-proposal-export-namespace-from/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.5 + dev: true + /@babel/plugin-proposal-json-strings/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg==} engines: {node: '>=6.9.0'} @@ -1096,6 +1230,17 @@ packages: '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.18.2 dev: true + /@babel/plugin-proposal-json-strings/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.5 + dev: true + /@babel/plugin-proposal-logical-assignment-operators/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q==} engines: {node: '>=6.9.0'} @@ -1107,6 +1252,17 @@ packages: '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.18.2 dev: true + /@babel/plugin-proposal-logical-assignment-operators/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.5 + dev: true + /@babel/plugin-proposal-nullish-coalescing-operator/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag==} engines: {node: '>=6.9.0'} @@ -1118,6 +1274,17 @@ packages: '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.18.2 dev: true + /@babel/plugin-proposal-nullish-coalescing-operator/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.5 + dev: true + /@babel/plugin-proposal-numeric-separator/7.16.7_@babel+core@7.18.2: resolution: {integrity: sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==} engines: {node: '>=6.9.0'} @@ -1129,6 +1296,17 @@ packages: '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.18.2 dev: true + /@babel/plugin-proposal-numeric-separator/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.5 + dev: true + /@babel/plugin-proposal-object-rest-spread/7.18.0_@babel+core@7.18.2: resolution: {integrity: sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw==} engines: {node: '>=6.9.0'} @@ -1143,6 +1321,20 @@ packages: '@babel/plugin-transform-parameters': 7.17.12_@babel+core@7.18.2 dev: true + /@babel/plugin-proposal-object-rest-spread/7.18.0_@babel+core@7.20.5: + resolution: {integrity: sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.20.5 + '@babel/core': 7.20.5 + '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-transform-parameters': 7.17.12_@babel+core@7.20.5 + dev: true + /@babel/plugin-proposal-optional-catch-binding/7.16.7_@babel+core@7.18.2: resolution: {integrity: sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==} engines: {node: '>=6.9.0'} @@ -1154,6 +1346,17 @@ packages: '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.18.2 dev: true + /@babel/plugin-proposal-optional-catch-binding/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.5 + dev: true + /@babel/plugin-proposal-optional-chaining/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ==} engines: {node: '>=6.9.0'} @@ -1166,6 +1369,18 @@ packages: '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.18.2 dev: true + /@babel/plugin-proposal-optional-chaining/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.5 + dev: true + /@babel/plugin-proposal-private-methods/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A==} engines: {node: '>=6.9.0'} @@ -1179,8 +1394,21 @@ packages: - supports-color dev: true - /@babel/plugin-proposal-private-property-in-object/7.17.12_@babel+core@7.18.2: - resolution: {integrity: sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg==} + /@babel/plugin-proposal-private-methods/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-create-class-features-plugin': 7.20.5_@babel+core@7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-private-property-in-object/7.17.12_@babel+core@7.18.2: + resolution: {integrity: sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1194,6 +1422,21 @@ packages: - supports-color dev: true + /@babel/plugin-proposal-private-property-in-object/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-create-class-features-plugin': 7.20.5_@babel+core@7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.5 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-unicode-property-regex/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A==} engines: {node: '>=4'} @@ -1205,6 +1448,17 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-proposal-unicode-property-regex/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A==} + engines: {node: '>=4'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-create-regexp-features-plugin': 7.17.12_@babel+core@7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.18.2: resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -1269,6 +1523,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.20.5: + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.18.2: resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: @@ -1278,6 +1542,15 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.20.5: + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.18.2: resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: @@ -1287,6 +1560,15 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.20.5: + resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-syntax-import-assertions/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw==} engines: {node: '>=6.9.0'} @@ -1297,6 +1579,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-syntax-import-assertions/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.18.2: resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: @@ -1461,6 +1753,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.20.5: + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.18.2: resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} @@ -1491,6 +1793,16 @@ packages: '@babel/helper-plugin-utils': 7.17.12 dev: true + /@babel/plugin-syntax-typescript/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.17.12 + dev: true + /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.20.5: resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} engines: {node: '>=6.9.0'} @@ -1511,6 +1823,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-arrow-functions/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-async-to-generator/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ==} engines: {node: '>=6.9.0'} @@ -1525,6 +1847,20 @@ packages: - supports-color dev: true + /@babel/plugin-transform-async-to-generator/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-remap-async-to-generator': 7.16.8 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-block-scoped-functions/7.16.7_@babel+core@7.18.2: resolution: {integrity: sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==} engines: {node: '>=6.9.0'} @@ -1535,6 +1871,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-block-scoped-functions/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-block-scoping/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-jw8XW/B1i7Lqwqj2CbrViPcZijSxfguBWZP2aN59NHgxUyO/OcO1mfdCxH13QhN5LbWhPkX+f+brKGhZTiqtZQ==} engines: {node: '>=6.9.0'} @@ -1545,6 +1891,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-block-scoping/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-jw8XW/B1i7Lqwqj2CbrViPcZijSxfguBWZP2aN59NHgxUyO/OcO1mfdCxH13QhN5LbWhPkX+f+brKGhZTiqtZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-classes/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-cvO7lc7pZat6BsvH6l/EGaI8zpl8paICaoGk+7x7guvtfak/TbIf66nYmJOH13EuG0H+Xx3M+9LQDtSvZFKXKw==} engines: {node: '>=6.9.0'} @@ -1564,6 +1920,25 @@ packages: - supports-color dev: true + /@babel/plugin-transform-classes/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-cvO7lc7pZat6BsvH6l/EGaI8zpl8paICaoGk+7x7guvtfak/TbIf66nYmJOH13EuG0H+Xx3M+9LQDtSvZFKXKw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-replace-supers': 7.19.1 + '@babel/helper-split-export-declaration': 7.18.6 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-computed-properties/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ==} engines: {node: '>=6.9.0'} @@ -1574,6 +1949,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-computed-properties/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-destructuring/7.18.0_@babel+core@7.18.2: resolution: {integrity: sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw==} engines: {node: '>=6.9.0'} @@ -1584,6 +1969,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-destructuring/7.18.0_@babel+core@7.20.5: + resolution: {integrity: sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-dotall-regex/7.16.7_@babel+core@7.18.2: resolution: {integrity: sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==} engines: {node: '>=6.9.0'} @@ -1595,6 +1990,17 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-dotall-regex/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-create-regexp-features-plugin': 7.17.12_@babel+core@7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-duplicate-keys/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw==} engines: {node: '>=6.9.0'} @@ -1605,6 +2011,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-duplicate-keys/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-exponentiation-operator/7.16.7_@babel+core@7.18.2: resolution: {integrity: sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==} engines: {node: '>=6.9.0'} @@ -1616,6 +2032,17 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-exponentiation-operator/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.16.7 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-for-of/7.18.1_@babel+core@7.18.2: resolution: {integrity: sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg==} engines: {node: '>=6.9.0'} @@ -1626,6 +2053,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-for-of/7.18.1_@babel+core@7.20.5: + resolution: {integrity: sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-function-name/7.16.7_@babel+core@7.18.2: resolution: {integrity: sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==} engines: {node: '>=6.9.0'} @@ -1638,6 +2075,18 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-function-name/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.5 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-literals/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ==} engines: {node: '>=6.9.0'} @@ -1648,6 +2097,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-literals/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-member-expression-literals/7.16.7_@babel+core@7.18.2: resolution: {integrity: sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==} engines: {node: '>=6.9.0'} @@ -1658,6 +2117,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-member-expression-literals/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-modules-amd/7.18.0_@babel+core@7.18.2: resolution: {integrity: sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA==} engines: {node: '>=6.9.0'} @@ -1672,6 +2141,20 @@ packages: - supports-color dev: true + /@babel/plugin-transform-modules-amd/7.18.0_@babel+core@7.20.5: + resolution: {integrity: sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-module-transforms': 7.20.2 + '@babel/helper-plugin-utils': 7.20.2 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-modules-commonjs/7.17.7_@babel+core@7.18.2: resolution: {integrity: sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==} engines: {node: '>=6.9.0'} @@ -1687,6 +2170,21 @@ packages: - supports-color dev: true + /@babel/plugin-transform-modules-commonjs/7.17.7_@babel+core@7.20.5: + resolution: {integrity: sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-module-transforms': 7.19.0 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-simple-access': 7.18.2 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-modules-commonjs/7.18.2_@babel+core@7.18.2: resolution: {integrity: sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ==} engines: {node: '>=6.9.0'} @@ -1702,6 +2200,21 @@ packages: - supports-color dev: true + /@babel/plugin-transform-modules-commonjs/7.18.2_@babel+core@7.20.5: + resolution: {integrity: sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-module-transforms': 7.20.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-simple-access': 7.20.2 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-modules-systemjs/7.18.0_@babel+core@7.18.2: resolution: {integrity: sha512-vwKpxdHnlM5tIrRt/eA0bzfbi7gUBLN08vLu38np1nZevlPySRe6yvuATJB5F/WPJ+ur4OXwpVYq9+BsxqAQuQ==} engines: {node: '>=6.9.0'} @@ -1718,6 +2231,22 @@ packages: - supports-color dev: true + /@babel/plugin-transform-modules-systemjs/7.18.0_@babel+core@7.20.5: + resolution: {integrity: sha512-vwKpxdHnlM5tIrRt/eA0bzfbi7gUBLN08vLu38np1nZevlPySRe6yvuATJB5F/WPJ+ur4OXwpVYq9+BsxqAQuQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-module-transforms': 7.20.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-validator-identifier': 7.19.1 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-modules-umd/7.18.0_@babel+core@7.18.2: resolution: {integrity: sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA==} engines: {node: '>=6.9.0'} @@ -1731,6 +2260,19 @@ packages: - supports-color dev: true + /@babel/plugin-transform-modules-umd/7.18.0_@babel+core@7.20.5: + resolution: {integrity: sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-module-transforms': 7.20.2 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-named-capturing-groups-regex/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA==} engines: {node: '>=6.9.0'} @@ -1742,62 +2284,189 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-named-capturing-groups-regex/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-create-regexp-features-plugin': 7.17.12_@babel+core@7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-new-target/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-CaOtzk2fDYisbjAD4Sd1MTKGVIpRtx9bWLyj24Y/k6p4s4gQ3CqDGJauFJxt8M/LEx003d0i3klVqnN73qvK3w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.18.2 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-new-target/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-CaOtzk2fDYisbjAD4Sd1MTKGVIpRtx9bWLyj24Y/k6p4s4gQ3CqDGJauFJxt8M/LEx003d0i3klVqnN73qvK3w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-object-super/7.16.7_@babel+core@7.18.2: + resolution: {integrity: sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.18.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-replace-supers': 7.19.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-object-super/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-replace-supers': 7.19.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-parameters/7.17.12_@babel+core@7.18.2: + resolution: {integrity: sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.18.2 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-parameters/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-property-literals/7.16.7_@babel+core@7.18.2: + resolution: {integrity: sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.18.2 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-property-literals/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-regenerator/7.18.0_@babel+core@7.18.2: + resolution: {integrity: sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.18.2 + '@babel/helper-plugin-utils': 7.20.2 + regenerator-transform: 0.15.0 + dev: true + + /@babel/plugin-transform-regenerator/7.18.0_@babel+core@7.20.5: + resolution: {integrity: sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + regenerator-transform: 0.15.0 + dev: true + + /@babel/plugin-transform-reserved-words/7.17.12_@babel+core@7.18.2: + resolution: {integrity: sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.18.2 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-reserved-words/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-object-super/7.16.7_@babel+core@7.18.2: - resolution: {integrity: sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==} + /@babel/plugin-transform-shorthand-properties/7.16.7_@babel+core@7.18.2: + resolution: {integrity: sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.19.1 - transitivePeerDependencies: - - supports-color dev: true - /@babel/plugin-transform-parameters/7.17.12_@babel+core@7.18.2: - resolution: {integrity: sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA==} + /@babel/plugin-transform-shorthand-properties/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.20.5 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-property-literals/7.16.7_@babel+core@7.18.2: - resolution: {integrity: sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==} + /@babel/plugin-transform-spread/7.17.12_@babel+core@7.18.2: + resolution: {integrity: sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 dev: true - /@babel/plugin-transform-regenerator/7.18.0_@babel+core@7.18.2: - resolution: {integrity: sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw==} + /@babel/plugin-transform-spread/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.20.5 '@babel/helper-plugin-utils': 7.20.2 - regenerator-transform: 0.15.0 + '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 dev: true - /@babel/plugin-transform-reserved-words/7.17.12_@babel+core@7.18.2: - resolution: {integrity: sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA==} + /@babel/plugin-transform-sticky-regex/7.16.7_@babel+core@7.18.2: + resolution: {integrity: sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1806,39 +2475,38 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-shorthand-properties/7.16.7_@babel+core@7.18.2: - resolution: {integrity: sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==} + /@babel/plugin-transform-sticky-regex/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.20.5 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-spread/7.17.12_@babel+core@7.18.2: - resolution: {integrity: sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg==} + /@babel/plugin-transform-template-literals/7.18.2_@babel+core@7.18.2: + resolution: {integrity: sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.2 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 dev: true - /@babel/plugin-transform-sticky-regex/7.16.7_@babel+core@7.18.2: - resolution: {integrity: sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==} + /@babel/plugin-transform-template-literals/7.18.2_@babel+core@7.20.5: + resolution: {integrity: sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.20.5 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-template-literals/7.18.2_@babel+core@7.18.2: - resolution: {integrity: sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g==} + /@babel/plugin-transform-typeof-symbol/7.17.12_@babel+core@7.18.2: + resolution: {integrity: sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1847,13 +2515,13 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-typeof-symbol/7.17.12_@babel+core@7.18.2: + /@babel/plugin-transform-typeof-symbol/7.17.12_@babel+core@7.20.5: resolution: {integrity: sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.20.5 '@babel/helper-plugin-utils': 7.20.2 dev: true @@ -1871,6 +2539,20 @@ packages: - supports-color dev: true + /@babel/plugin-transform-typescript/7.18.4_@babel+core@7.20.5: + resolution: {integrity: sha512-l4vHuSLUajptpHNEOUDEGsnpl9pfRLsN1XUoDQDD/YBuXTM+v37SHGS+c6n4jdcZy96QtuUuSvZYMLSSsjH8Mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-create-class-features-plugin': 7.18.0_@babel+core@7.20.5 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/plugin-syntax-typescript': 7.17.12_@babel+core@7.20.5 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-typescript/7.20.2_@babel+core@7.20.5: resolution: {integrity: sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag==} engines: {node: '>=6.9.0'} @@ -1895,6 +2577,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-unicode-escapes/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-unicode-regex/7.16.7_@babel+core@7.18.2: resolution: {integrity: sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==} engines: {node: '>=6.9.0'} @@ -1906,6 +2598,17 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-transform-unicode-regex/7.16.7_@babel+core@7.20.5: + resolution: {integrity: sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-create-regexp-features-plugin': 7.17.12_@babel+core@7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/preset-env/7.18.2_@babel+core@7.18.2: resolution: {integrity: sha512-PfpdxotV6afmXMU47S08F9ZKIm2bJIQ0YbAAtDfIENX7G1NUAXigLREh69CWDjtgUy7dYn7bsMzkgdtAlmS68Q==} engines: {node: '>=6.9.0'} @@ -1992,6 +2695,92 @@ packages: - supports-color dev: true + /@babel/preset-env/7.18.2_@babel+core@7.20.5: + resolution: {integrity: sha512-PfpdxotV6afmXMU47S08F9ZKIm2bJIQ0YbAAtDfIENX7G1NUAXigLREh69CWDjtgUy7dYn7bsMzkgdtAlmS68Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.20.5 + '@babel/core': 7.20.5 + '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-validator-option': 7.18.6 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-proposal-async-generator-functions': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-proposal-class-properties': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-proposal-class-static-block': 7.18.0_@babel+core@7.20.5 + '@babel/plugin-proposal-dynamic-import': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-proposal-export-namespace-from': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-proposal-json-strings': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-proposal-logical-assignment-operators': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-proposal-numeric-separator': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-proposal-object-rest-spread': 7.18.0_@babel+core@7.20.5 + '@babel/plugin-proposal-optional-catch-binding': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-proposal-optional-chaining': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-proposal-private-methods': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-proposal-private-property-in-object': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-proposal-unicode-property-regex': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.5 + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.5 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.5 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-import-assertions': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.5 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.5 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.5 + '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.5 + '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.5 + '@babel/plugin-transform-arrow-functions': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-async-to-generator': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-block-scoped-functions': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-transform-block-scoping': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-classes': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-computed-properties': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-destructuring': 7.18.0_@babel+core@7.20.5 + '@babel/plugin-transform-dotall-regex': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-transform-duplicate-keys': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-exponentiation-operator': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-transform-for-of': 7.18.1_@babel+core@7.20.5 + '@babel/plugin-transform-function-name': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-transform-literals': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-member-expression-literals': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-transform-modules-amd': 7.18.0_@babel+core@7.20.5 + '@babel/plugin-transform-modules-commonjs': 7.18.2_@babel+core@7.20.5 + '@babel/plugin-transform-modules-systemjs': 7.18.0_@babel+core@7.20.5 + '@babel/plugin-transform-modules-umd': 7.18.0_@babel+core@7.20.5 + '@babel/plugin-transform-named-capturing-groups-regex': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-new-target': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-object-super': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-transform-parameters': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-property-literals': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-transform-regenerator': 7.18.0_@babel+core@7.20.5 + '@babel/plugin-transform-reserved-words': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-shorthand-properties': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-transform-spread': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-sticky-regex': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-transform-template-literals': 7.18.2_@babel+core@7.20.5 + '@babel/plugin-transform-typeof-symbol': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-unicode-escapes': 7.16.7_@babel+core@7.20.5 + '@babel/plugin-transform-unicode-regex': 7.16.7_@babel+core@7.20.5 + '@babel/preset-modules': 0.1.5_@babel+core@7.20.5 + '@babel/types': 7.20.5 + babel-plugin-polyfill-corejs2: 0.3.0_@babel+core@7.20.5 + babel-plugin-polyfill-corejs3: 0.5.2_@babel+core@7.20.5 + babel-plugin-polyfill-regenerator: 0.3.0_@babel+core@7.20.5 + core-js-compat: 3.22.7 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/preset-modules/0.1.5_@babel+core@7.18.2: resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} peerDependencies: @@ -2005,6 +2794,19 @@ packages: esutils: 2.0.3 dev: true + /@babel/preset-modules/0.1.5_@babel+core@7.20.5: + resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-proposal-unicode-property-regex': 7.17.12_@babel+core@7.20.5 + '@babel/plugin-transform-dotall-regex': 7.16.7_@babel+core@7.20.5 + '@babel/types': 7.20.5 + esutils: 2.0.3 + dev: true + /@babel/preset-typescript/7.17.12_@babel+core@7.18.2: resolution: {integrity: sha512-S1ViF8W2QwAKUGJXxP9NAfNaqGDdEBJKpYkxHf5Yy2C4NPPzXGeR3Lhk7G8xJaaLcFTRfNjVbtbVtm8Gb0mqvg==} engines: {node: '>=6.9.0'} @@ -2019,6 +2821,20 @@ packages: - supports-color dev: true + /@babel/preset-typescript/7.17.12_@babel+core@7.20.5: + resolution: {integrity: sha512-S1ViF8W2QwAKUGJXxP9NAfNaqGDdEBJKpYkxHf5Yy2C4NPPzXGeR3Lhk7G8xJaaLcFTRfNjVbtbVtm8Gb0mqvg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-plugin-utils': 7.17.12 + '@babel/helper-validator-option': 7.16.7 + '@babel/plugin-transform-typescript': 7.18.4_@babel+core@7.20.5 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/runtime/7.16.7: resolution: {integrity: sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==} engines: {node: '>=6.9.0'} @@ -2575,7 +3391,7 @@ packages: istanbul-lib-instrument: 5.2.1 istanbul-lib-report: 3.0.0 istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.3 + istanbul-reports: 3.1.5 jest-haste-map: 27.5.1 jest-resolve: 27.5.1 jest-util: 27.5.1 @@ -3169,7 +3985,7 @@ packages: consola: 2.15.3 fast-glob: 3.2.12 magic-string: 0.27.0 - pathe: 1.0.0 + pathe: 1.1.0 perfect-debounce: 0.1.3 transitivePeerDependencies: - rollup @@ -3477,6 +4293,37 @@ packages: - supports-color dev: true + /@vue/vue3-jest/27.0.0_rggaoj2gxgtz66f7k5b6ywrwv4: + resolution: {integrity: sha512-VL61CgZBoQqayXfzlZJHHpZuX4lsT8dmdZMJzADhdAJjKu26JBpypHr/2ppevxItljPiuALQW4MKhhCXZRXnLg==} + peerDependencies: + '@babel/core': 7.x + babel-jest: 27.x + jest: 27.x + ts-jest: 27.x + typescript: '>= 3.x' + vue: ^3.0.0-0 + peerDependenciesMeta: + ts-jest: + optional: true + typescript: + optional: true + dependencies: + '@babel/core': 7.20.5 + '@babel/plugin-transform-modules-commonjs': 7.17.7_@babel+core@7.20.5 + babel-jest: 27.5.1_@babel+core@7.20.5 + chalk: 2.4.2 + convert-source-map: 1.8.0 + css-tree: 2.1.0 + jest: 27.5.1 + source-map: 0.5.6 + ts-jest: 27.1.5_lymxezh3rizc3sf4nv2timwyqu + tsconfig: 7.0.0 + typescript: 4.9.4 + vue: 3.2.45 + transitivePeerDependencies: + - supports-color + dev: true + /@vueuse/core/9.10.0_vue@3.2.45: resolution: {integrity: sha512-CxMewME07qeuzuT/AOIQGv0EhhDoojniqU6pC3F8m5VC76L47UT18DcX88kWlP3I7d3qMJ4u/PD8iSRsy3bmNA==} dependencies: @@ -3771,6 +4618,19 @@ packages: - supports-color dev: true + /babel-plugin-polyfill-corejs2/0.3.0_@babel+core@7.20.5: + resolution: {integrity: sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.20.5 + '@babel/core': 7.20.5 + '@babel/helper-define-polyfill-provider': 0.3.1_@babel+core@7.20.5 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-polyfill-corejs3/0.5.2_@babel+core@7.18.2: resolution: {integrity: sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==} peerDependencies: @@ -3783,6 +4643,18 @@ packages: - supports-color dev: true + /babel-plugin-polyfill-corejs3/0.5.2_@babel+core@7.20.5: + resolution: {integrity: sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-define-polyfill-provider': 0.3.1_@babel+core@7.20.5 + core-js-compat: 3.22.7 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-polyfill-regenerator/0.3.0_@babel+core@7.18.2: resolution: {integrity: sha512-dhAPTDLGoMW5/84wkgwiLRwMnio2i1fUe53EuvtKMv0pn2p3S8OCoV1xAzfJPl0KOX7IB89s2ib85vbYiea3jg==} peerDependencies: @@ -3794,6 +4666,17 @@ packages: - supports-color dev: true + /babel-plugin-polyfill-regenerator/0.3.0_@babel+core@7.20.5: + resolution: {integrity: sha512-dhAPTDLGoMW5/84wkgwiLRwMnio2i1fUe53EuvtKMv0pn2p3S8OCoV1xAzfJPl0KOX7IB89s2ib85vbYiea3jg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.20.5 + '@babel/helper-define-polyfill-provider': 0.3.1_@babel+core@7.20.5 + transitivePeerDependencies: + - supports-color + dev: true + /babel-preset-current-node-syntax/1.0.1_@babel+core@7.18.2: resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: @@ -4141,7 +5024,7 @@ packages: wrap-ansi: 7.0.0 /co/4.6.0: - resolution: {integrity: sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=} + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} dev: true @@ -4486,7 +5369,7 @@ packages: dev: true /dedent/0.7.0: - resolution: {integrity: sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=} + resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true /deep-eql/4.1.3: @@ -5216,7 +6099,7 @@ packages: dev: true /exit/0.1.2: - resolution: {integrity: sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=} + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} dev: true @@ -5705,7 +6588,7 @@ packages: dev: true /imurmurhash/0.1.4: - resolution: {integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=} + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} dev: true @@ -5737,7 +6620,7 @@ packages: dev: true /is-arrayish/0.2.1: - resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=} + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true /is-bigint/1.0.4: @@ -5931,8 +6814,8 @@ packages: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} dependencies: - '@babel/core': 7.19.3 - '@babel/parser': 7.19.3 + '@babel/core': 7.20.5 + '@babel/parser': 7.20.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.0 @@ -5957,21 +6840,12 @@ packages: transitivePeerDependencies: - supports-color - /istanbul-reports/3.1.3: - resolution: {integrity: sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==} - engines: {node: '>=8'} - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.0 - dev: true - /istanbul-reports/3.1.5: resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} engines: {node: '>=8'} dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.0 - dev: false /jest-changed-files/27.5.1: resolution: {integrity: sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==} @@ -6757,6 +7631,12 @@ packages: get-func-name: 2.0.0 dev: false + /loupe/2.3.6: + resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + dependencies: + get-func-name: 2.0.0 + dev: false + /lru-cache/6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -6903,7 +7783,7 @@ packages: hasBin: true /natural-compare/1.4.0: - resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=} + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true /node-addon-api/5.0.0: @@ -6932,7 +7812,7 @@ packages: dev: true /node-int64/0.4.0: - resolution: {integrity: sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=} + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true /node-releases/2.0.6: @@ -7050,7 +7930,6 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: yocto-queue: 1.0.0 - dev: true /p-locate/4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} @@ -7134,12 +8013,12 @@ packages: /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - /pathe/0.2.0: - resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} - /pathe/1.0.0: resolution: {integrity: sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==} + /pathe/1.1.0: + resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} + /pathval/1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: false @@ -7185,7 +8064,7 @@ packages: dependencies: jsonc-parser: 3.2.0 mlly: 1.1.0 - pathe: 1.0.0 + pathe: 1.1.0 /platform/1.3.6: resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} @@ -7327,7 +8206,7 @@ packages: dev: true /require-directory/2.1.1: - resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} /requires-port/1.0.0: @@ -7496,6 +8375,10 @@ packages: object-inspect: 1.12.0 dev: true + /siginfo/2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: false + /signal-exit/3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -7552,7 +8435,7 @@ packages: source-map: 0.6.1 /source-map/0.5.6: - resolution: {integrity: sha1-dc449SvwczxafwwRjYEzSiu19BI=} + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} engines: {node: '>=0.10.0'} dev: true @@ -7574,7 +8457,7 @@ packages: dev: true /sprintf-js/1.0.3: - resolution: {integrity: sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=} + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true /sshpk/1.17.0: @@ -7600,6 +8483,14 @@ packages: escape-string-regexp: 2.0.0 dev: true + /stackback/0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: false + + /std-env/3.3.1: + resolution: {integrity: sha512-3H20QlwQsSm2OvAxWIYhs+j01MzzqwMwGiiO1NQaJYZgJZFPuAbf95/DiKRBSTYIJ2FeGUc+B/6mPGcWP9dO3Q==} + dev: false + /streamsearch/1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -7642,7 +8533,7 @@ packages: ansi-regex: 6.0.1 /strip-bom/3.0.0: - resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=} + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} dev: true @@ -7662,7 +8553,7 @@ packages: dev: true /strip-json-comments/2.0.1: - resolution: {integrity: sha1-PFMZQukIwml8DsNEhYwobHygpgo=} + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} dev: true @@ -7853,6 +8744,41 @@ packages: yargs-parser: 20.2.9 dev: true + /ts-jest/27.1.5_lymxezh3rizc3sf4nv2timwyqu: + resolution: {integrity: sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@types/jest': ^27.0.0 + babel-jest: '>=27.0.0 <28' + esbuild: '*' + jest: ^27.0.0 + typescript: '>=3.8 <5.0' + peerDependenciesMeta: + '@babel/core': + optional: true + '@types/jest': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.20.5 + babel-jest: 27.5.1_@babel+core@7.20.5 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 27.5.1 + jest-util: 27.5.1 + json5: 2.2.1 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.3.5 + typescript: 4.9.4 + yargs-parser: 20.2.9 + dev: true + /tsconfig/7.0.0: resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} dependencies: @@ -7979,7 +8905,7 @@ packages: local-pkg: 0.4.2 magic-string: 0.27.0 mlly: 1.1.0 - pathe: 1.0.0 + pathe: 1.1.0 pkg-types: 1.0.1 scule: 1.0.0 strip-literal: 1.0.0 @@ -8314,6 +9240,7 @@ packages: /w3c-hr-time/1.0.2: resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} + deprecated: Use your platform's native performance.now() and performance.timeOrigin. dependencies: browser-process-hrtime: 1.0.0 dev: true @@ -8450,6 +9377,15 @@ packages: dependencies: isexe: 2.0.0 + /why-is-node-running/2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: false + /word-wrap/1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} @@ -8574,4 +9510,3 @@ packages: /yocto-queue/1.0.0: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} - dev: true diff --git a/bench/src/bench.ts b/bench/src/bench.ts index ea99804900a5..0fec41613888 100644 --- a/bench/src/bench.ts +++ b/bench/src/bench.ts @@ -72,7 +72,7 @@ export function runBench(callback: (data: Result[]) => void) { defer: true, fn: (deferred: Deferred) => execa('pnpm', ['test:vitest'], execaOptions) .on('exit', (code) => { - if (code > 0) + if (code) exit(suite, code) else deferred.resolve() @@ -83,7 +83,7 @@ export function runBench(callback: (data: Result[]) => void) { defer: true, fn: (deferred: Deferred) => execa('pnpm', ['test:jest'], execaOptions) .on('exit', (code) => { - if (code > 0) + if (code) exit(suite, code) else deferred.resolve() @@ -94,8 +94,8 @@ export function runBench(callback: (data: Result[]) => void) { bench.on('complete', () => { const results = bench .map((run: Target): Result => ({ - name: run.name, - ...run.stats, + name: run.name!, + ...run.stats!, })) .sort((a, b) => { return a.mean - b.mean }) diff --git a/bench/test/vue/jest.config.js b/bench/test/vue/jest.config.js index 81d292924c2a..e00d4e0c0c5c 100644 --- a/bench/test/vue/jest.config.js +++ b/bench/test/vue/jest.config.js @@ -5,6 +5,7 @@ module.exports = { 'ts', 'vue', ], + setupFiles: ['./setup.jest.js'], transform: { '.*\\.(vue)$': '@vue/vue3-jest', '.*\\.(ts)$': 'babel-jest', diff --git a/bench/test/vue/setup.jest.js b/bench/test/vue/setup.jest.js new file mode 100644 index 000000000000..ace9bcf73358 --- /dev/null +++ b/bench/test/vue/setup.jest.js @@ -0,0 +1,3 @@ +// eslint-disable-next-line no-undef +globalThis.vi = jest +globalThis.vi.dynamicImportSettled = () => Promise.resolve() diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 981a5366006b..287a605fd52b 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -85,6 +85,7 @@ export default withPwa(defineConfig({ { text: 'Guide', link: '/guide/' }, { text: 'API', link: '/api/' }, { text: 'Config', link: '/config/' }, + { text: 'Advanced', link: '/advanced/api' }, { text: `v${version}`, items: [ @@ -102,6 +103,21 @@ export default withPwa(defineConfig({ sidebar: { // TODO: bring sidebar of apis and config back + '/advanced': [ + { + text: 'Advanced', + items: [ + { + text: 'Vitest Node API', + link: '/advanced/api', + }, + { + text: 'Runner API', + link: '/advanced/runner', + }, + ], + }, + ], '/': [ { text: 'Guide', diff --git a/docs/advanced/api.md b/docs/advanced/api.md new file mode 100644 index 000000000000..ce36b7ae8fde --- /dev/null +++ b/docs/advanced/api.md @@ -0,0 +1,67 @@ +# Node API + +::: warning +Vitest exposes experimental private API. Breaking changes might not follow semver, please pin Vitest's version when using it. +::: + +## startVitest + +You can start running Vitest tests using its Node API: + +```js +import { startVitest } from 'vitest/node' + +const vitest = await startVitest('test', ['tests/run-only.test.ts']) + +await vitest?.close() +``` + +`startVitest` function returns `Vitest` instance if tests can be started. It returns `undefined`, if one of the following occurs: + +- Vitest didn't find "vite" package (usually installed with Vitest) +- If coverage is enabled and run mode is "test", but the coverage package is not installed (`@vitest/coverage-c8` or `@vitest/coverage-istanbul`) +- If the environment package is not installed (`jsdom`/`happy-dom`/`@edge-runtime/vm`) + +If `undefined` is returned or tests failed during the run, Vitest sets `process.exitCode` to `1`. + +If watch mode is not enabled, Vitest will call `close` method. + +If watch mode is enabled and the terminal supports TTY, Vitest will register console shortcuts. + +## createVitest + +You can create Vitest instance yourself using `createVitest` function. It returns the same `Vitest` instance as `startVitest`, but it doesn't start tests and doesn't validate installed packages. + +```js +import { createVitest } from 'vitest/node' + +const vitest = await createVitest('test', { + watch: false, +}) +``` + +## Vitest + +Vitest instance requires the current test mode. I can be either: + +- `test` when running runtime tests +- `benchmark` when running benchmarks +- `typecheck` when running type tests + +### mode + +#### test + +Test mode will only call functions inside `test` or `it`, and throws an error when `bench` is encountered. This mode uses `include` and `exclude` options in the config to find test files. + +#### benchmark + +Benchmark mode calls `bench` functions and throws an error, when it encounters `test` or `it`. This mode uses `benchmark.include` and `benchmark.exclude` options in the config to find benchmark files. + +#### typecheck + +Typecheck mode doesn't _run_ tests. It only analyses types and gives a summary. This mode uses `typecheck.include` and `typecheck.exclude` options in the config to find files to analyze. + +### start + +You can start running tests or benchmarks with `start` method. You can pass an array of strings to filter test files. diff --git a/docs/advanced/runner.md b/docs/advanced/runner.md new file mode 100644 index 000000000000..5d45c023e21d --- /dev/null +++ b/docs/advanced/runner.md @@ -0,0 +1,148 @@ +# Test Runner + +::: warning +This is advanced API. If you are just running tests, you probably don't need this. It is primarily used by library authors. +::: + +You can specify a path to your test runner with the `runner` option in your configuration file. This file should have a default export with a class implementing these methods: + +```ts +export interface VitestRunner { + /** + * First thing that's getting called before actually collecting and running tests. + */ + onBeforeCollect?(paths: string[]): unknown + /** + * Called after collecting tests and before "onBeforeRun". + */ + onCollected?(files: File[]): unknown + + /** + * Called before running a single test. Doesn't have "result" yet. + */ + onBeforeRunTest?(test: Test): unknown + /** + * Called before actually running the test function. Already has "result" with "state" and "startTime". + */ + onBeforeTryTest?(test: Test, retryCount: number): unknown + /** + * Called after result and state are set. + */ + onAfterRunTest?(test: Test): unknown + /** + * Called right after running the test function. Doesn't have new state yet. Will not be called, if the test function throws. + */ + onAfterTryTest?(test: Test, retryCount: number): unknown + + /** + * Called before running a single suite. Doesn't have "result" yet. + */ + onBeforeRunSuite?(suite: Suite): unknown + /** + * Called after running a single suite. Has state and result. + */ + onAfterRunSuite?(suite: Suite): unknown + + /** + * If defined, will be called instead of usual Vitest suite partition and handling. + * "before" and "after" hooks will not be ignored. + */ + runSuite?(suite: Suite): Promise + /** + * If defined, will be called instead of usual Vitest handling. Useful, if you have your custom test function. + * "before" and "after" hooks will not be ignored. + */ + runTest?(test: Test): Promise + + /** + * Called, when a task is updated. The same as "onTaskUpdate" in a reporter, but this is running in the same thread as tests. + */ + onTaskUpdate?(task: [string, TaskResult | undefined][]): Promise + + /** + * Called before running all tests in collected paths. + */ + onBeforeRun?(files: File[]): unknown + /** + * Called right after running all tests in collected paths. + */ + onAfterRun?(files: File[]): unknown + /** + * Called when new context for a test is defined. Useful, if you want to add custom properties to the context. + * If you only want to define custom context with a runner, consider using "beforeAll" in "setupFiles" instead. + */ + extendTestContext?(context: TestContext): TestContext + /** + * Called, when files are imported. Can be called in two situations: when collecting tests and when importing setup files. + */ + importFile(filepath: string, source: VitestRunnerImportSource): unknown + /** + * Publically available configuration. + */ + config: VitestRunnerConfig +} +``` + +When initiating this class, Vitest passes down Vitest config, - you should expose it as a `config` property. + +::: warning +`importFile` method in your custom runner must be inlined in `deps.inline` config option, if you call Node `import` inside. +::: + +::: tip +Snapshot support and some other features depend on the runner. If you don't want to lose it, you can extend your runner from `VitestTestRunner` imported from `vitest/runners`. It also exposes `BenchmarkNodeRunner`, if you want to extend benchmark functionality. +::: + +## Your task function + +You can extend Vitest task system with your tasks. A task is an object that is part of a suite. It is automatically added to the current suite with a `suite.custom` method: + +```js +// ./utils/custom.js +import { getCurrentSuite, setFn } from 'vitest/suite' +export { describe, beforeAll, afterAll } from 'vitest' + +// this function will be called, when Vitest collects tasks +export const myCustomTask = function (name, fn) { + const task = getCurrentSuite().custom(name) + task.meta = { + customPropertyToDifferentiateTask: true + } + setFn(task, fn || (() => {})) +} +``` + +```js +// ./garden/tasks.test.js +import { afterAll, beforeAll, describe, myCustomTask } from '../utils/custom.js' +import { gardener } from './gardener.js' + +deccribe('take care of the garden', () => { + beforeAll(() => { + gardener.putWorkingClothes() + }) + + myCustomTask('weed the grass', () => { + gardener.weedTheGrass() + }) + myCustomTask('water flowers', () => { + gardener.waterFlowers() + }) + + afterAll(() => { + gardener.goHome() + }) +}) +``` + +```bash +vitest ./garder/tasks.test.js +``` + +::: warning +If you don't have a custom runner or didn't define `runTest` method, Vitest will try to retrieve a task automatically. If you didn't add a function with `setFn`, it will fail. +::: + +::: tip +Custom task system supports hooks and contexts. If you want to support property chaining (like, `only`, `skip`, and your custom ones), you can import `createChainable` from `vitest/suite` and wrap your function with it. You will need to call `custom` as `custom.call(this)`, if you decide to do this. +::: diff --git a/docs/config/index.md b/docs/config/index.md index 0ba599bc7751..44490944558b 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -148,6 +148,13 @@ TypeError: default is not a function By default, Vitest assumes you are using a bundler to bypass this and will not fail, but you can disable this behaviour manually, if you code is not processed. +### runner + +- **Type**: `VitestRunnerConstructor` +- **Default**: `node`, when running tests, or `benchmark`, when running benchmarks + +Path to a custom test runner. This is an advanced feature and should be used with custom library runners. You can read more about it in [the documentation](/advanced/runner). + ### benchmark - **Type:** `{ include?, exclude?, ... }` diff --git a/docs/guide/ui.md b/docs/guide/ui.md index 7b2646868fff..18f3595c5f84 100644 --- a/docs/guide/ui.md +++ b/docs/guide/ui.md @@ -22,7 +22,7 @@ Then you can visit the Vitest UI at Vitest UI -Since Vitest 0.26.0, UI can also be used as a reporter. Use `'html'` reporter in your Vitest configuration to generate HTML output and preview results of your tests: +Since Vitest 0.26.0, UI can also be used as a reporter. Use `'html'` reporter in your Vitest configuration to generate HTML output and preview the results of your tests: ```ts // vitest.config.ts diff --git a/package.json b/package.json index c09f133d3973..b4891b83b11c 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "node-fetch-native": "^1.0.1", "npm-run-all": "^4.1.5", "ohmyfetch": "^0.4.21", - "pathe": "^0.2.0", + "pathe": "^1.1.0", "pnpm": "7.23.0", "rimraf": "^3.0.2", "rollup": "^2.79.1", diff --git a/packages/browser/package.json b/packages/browser/package.json index ff73f3c1b514..2527e8564142 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -34,6 +34,7 @@ "prepublishOnly": "pnpm build" }, "dependencies": { + "@vitest/runner": "workspace:*", "local-pkg": "^0.4.2", "mlly": "^1.1.0", "modern-node-polyfills": "0.0.9", diff --git a/packages/browser/src/client/main.ts b/packages/browser/src/client/main.ts index 9feb59d8967f..05ff52cc63ee 100644 --- a/packages/browser/src/client/main.ts +++ b/packages/browser/src/client/main.ts @@ -2,6 +2,9 @@ import type { VitestClient } from '@vitest/ws-client' import { createClient } from '@vitest/ws-client' // eslint-disable-next-line no-restricted-imports import type { ResolvedConfig } from 'vitest' +import type { VitestRunner } from '@vitest/runner' +import { createBrowserRunner } from './runner' +import { BrowserSnapshotEnvironment } from './snapshot' // @ts-expect-error mocking some node apis globalThis.process = { env: {}, argv: [], stdout: { write: () => {} } } @@ -14,6 +17,7 @@ export const ENTRY_URL = `${ }//${HOST}/__vitest_api__` let config: ResolvedConfig | undefined +let runner: VitestRunner | undefined const browserHashMap = new Map() export const client = createClient(ENTRY_URL, { @@ -21,13 +25,11 @@ export const client = createClient(ENTRY_URL, { async onPathsCollected(paths) { if (!paths) return - // const config = __vitest_worker__.config const now = `${new Date().getTime()}` paths.forEach((i) => { browserHashMap.set(i, now) }) - await runTests(paths, config, client) }, }, @@ -75,13 +77,26 @@ ws.addEventListener('open', async () => { await runTests(paths, config, client) }) +let hasSnapshot = false async function runTests(paths: string[], config: any, client: VitestClient) { - const name = '/__vitest_index__' - const { startTests, setupGlobalEnv } = (await import(name)) as unknown as typeof import('vitest/browser') - - await setupGlobalEnv(config as any) + // we use dynamic import here, because this file is bundled with UI, + // but we need to resolve correct path at runtime + const path = '/__vitest_index__' + const { startTests, setupCommonEnv, setupSnapshotEnvironment } = await import(path) as typeof import('vitest/browser') + + if (!runner) { + const runnerPath = '/__vitest_runners__' + const { VitestTestRunner } = await import(runnerPath) as typeof import('vitest/runners') + const BrowserRunner = createBrowserRunner(VitestTestRunner) + runner = new BrowserRunner({ config, client, browserHashMap }) + } - await startTests(paths, config as any) + if (!hasSnapshot) { + setupSnapshotEnvironment(new BrowserSnapshotEnvironment(client)) + hasSnapshot = true + } + await setupCommonEnv(config) + await startTests(paths, runner) await client.rpc.onFinished() await client.rpc.onWatcherStart() diff --git a/packages/browser/src/client/runner.ts b/packages/browser/src/client/runner.ts new file mode 100644 index 000000000000..b3f7f93fd465 --- /dev/null +++ b/packages/browser/src/client/runner.ts @@ -0,0 +1,41 @@ +import type { File, TaskResult } from '@vitest/runner' +import type { VitestClient } from '@vitest/ws-client' +import type { ResolvedConfig } from '#types' + +interface BrowserRunnerOptions { + config: ResolvedConfig + client: VitestClient + browserHashMap: Map +} + +export function createBrowserRunner(original: any) { + return class BrowserTestRunner extends original { + public config: ResolvedConfig + hasMap = new Map() + client: VitestClient + + constructor(options: BrowserRunnerOptions) { + super(options.config) + this.config = options.config + this.hasMap = options.browserHashMap + this.client = options.client + } + + onCollected(files: File[]): unknown { + return this.client.rpc.onCollected(files) + } + + onTaskUpdate(task: [string, TaskResult | undefined][]): Promise { + return this.client.rpc.onTaskUpdate(task) + } + + async importFile(filepath: string) { + const match = filepath.match(/^(\w:\/)/) + const hash = this.hasMap.get(filepath) + const importpath = match + ? `/@fs/${filepath.slice(match[1].length)}?v=${hash}` + : `${filepath}?v=${hash}` + await import(importpath) + } + } +} diff --git a/packages/browser/src/client/snapshot.ts b/packages/browser/src/client/snapshot.ts new file mode 100644 index 000000000000..d7b26a666364 --- /dev/null +++ b/packages/browser/src/client/snapshot.ts @@ -0,0 +1,26 @@ +import type { VitestClient } from '@vitest/ws-client' +import type { SnapshotEnvironment } from '#types' + +export class BrowserSnapshotEnvironment implements SnapshotEnvironment { + constructor(private client: VitestClient) {} + + readSnapshotFile(filepath: string): Promise { + return this.client.rpc.readFile(filepath) + } + + saveSnapshotFile(filepath: string, snapshot: string): Promise { + return this.client.rpc.writeFile(filepath, snapshot) + } + + resolvePath(filepath: string): Promise { + return this.client.rpc.resolveSnapshotPath(filepath) + } + + removeSnapshotFile(filepath: string): Promise { + return this.client.rpc.removeFile(filepath) + } + + async prepareDirectory(filepath: string): Promise { + await this.client.rpc.createDirectory(filepath) + } +} diff --git a/packages/browser/src/node/index.ts b/packages/browser/src/node/index.ts index 89fb2fa8e9c6..a2cfb830bbcd 100644 --- a/packages/browser/src/node/index.ts +++ b/packages/browser/src/node/index.ts @@ -5,23 +5,9 @@ import { builtinModules } from 'module' import { polyfillPath } from 'modern-node-polyfills' import sirv from 'sirv' import type { Plugin } from 'vite' -import { resolvePath } from 'mlly' - -const stubs = [ - 'fs', - 'local-pkg', - 'module', - 'noop', - 'perf_hooks', - 'console', -] const polyfills = [ 'util', - 'tty', - 'process', - 'path', - 'buffer', ] export default (base = '/'): Plugin[] => { @@ -36,13 +22,11 @@ export default (base = '/'): Plugin[] => { if (ctx.ssr) return - if (id === '/__vitest_index__') { - const result = await resolvePath('vitest/browser') - return result - } + if (id === '/__vitest_index__') + return this.resolve('vitest/browser') - if (stubs.includes(id)) - return resolve(pkgRoot, 'stubs', id) + if (id === '/__vitest_runners__') + return this.resolve('vitest/runners') if (polyfills.includes(id)) return polyfillPath(normalizeId(id)) diff --git a/packages/browser/stubs/console.js b/packages/browser/stubs/console.js deleted file mode 100644 index a0d1b5dfa55d..000000000000 --- a/packages/browser/stubs/console.js +++ /dev/null @@ -1 +0,0 @@ -export const Console = {} diff --git a/packages/browser/stubs/fs.js b/packages/browser/stubs/fs.js deleted file mode 100644 index e34b353c6868..000000000000 --- a/packages/browser/stubs/fs.js +++ /dev/null @@ -1,7 +0,0 @@ -import noop from './noop' - -export default noop -export { noop as promises } -export function existsSync() { - return false -} diff --git a/packages/browser/stubs/local-pkg.js b/packages/browser/stubs/local-pkg.js deleted file mode 100644 index 3805f3c3bdf7..000000000000 --- a/packages/browser/stubs/local-pkg.js +++ /dev/null @@ -1,2 +0,0 @@ -export const isPackageExists = () => {} -export const importModule = () => {} diff --git a/packages/browser/stubs/module.js b/packages/browser/stubs/module.js deleted file mode 100644 index 4e4b61833884..000000000000 --- a/packages/browser/stubs/module.js +++ /dev/null @@ -1 +0,0 @@ -export const createRequire = () => {} diff --git a/packages/browser/stubs/noop.js b/packages/browser/stubs/noop.js deleted file mode 100644 index 4351d1948045..000000000000 --- a/packages/browser/stubs/noop.js +++ /dev/null @@ -1,5 +0,0 @@ -export default new Proxy({}, { - get() { - throw new Error('Module has been externalized for browser compatibility and cannot be accessed in client code.') - }, -}) diff --git a/packages/browser/stubs/perf_hooks.js b/packages/browser/stubs/perf_hooks.js deleted file mode 100644 index fc54205432ba..000000000000 --- a/packages/browser/stubs/perf_hooks.js +++ /dev/null @@ -1 +0,0 @@ -export const performance = globalThis.performance diff --git a/packages/browser/stubs/tty.js b/packages/browser/stubs/tty.js deleted file mode 100644 index b4e4d2ca539f..000000000000 --- a/packages/browser/stubs/tty.js +++ /dev/null @@ -1,3 +0,0 @@ -export const isatty = () => false - -export default { isatty } diff --git a/packages/coverage-c8/package.json b/packages/coverage-c8/package.json index df5357c5f3a2..191acaa62e31 100644 --- a/packages/coverage-c8/package.json +++ b/packages/coverage-c8/package.json @@ -46,7 +46,7 @@ "vitest": "workspace:*" }, "devDependencies": { - "pathe": "^0.2.0", + "pathe": "^1.1.0", "vite-node": "workspace:*" } } diff --git a/packages/coverage-c8/src/provider.ts b/packages/coverage-c8/src/provider.ts index 7de1b28af7a6..5e5d2b5b8580 100644 --- a/packages/coverage-c8/src/provider.ts +++ b/packages/coverage-c8/src/provider.ts @@ -129,7 +129,7 @@ export class C8CoverageProvider implements CoverageProvider { // This is a magic number. It corresponds to the amount of code // that we add in packages/vite-node/src/client.ts:114 (vm.runInThisContext) // TODO: Include our transformations in sourcemaps - const offset = 203 + const offset = 185 report._getSourceMap = (coverage: Profiler.ScriptCoverage) => { const path = _url.pathToFileURL(coverage.url.split('?')[0]).href diff --git a/packages/coverage-istanbul/package.json b/packages/coverage-istanbul/package.json index 712573099163..dcaf7cfe1c22 100644 --- a/packages/coverage-istanbul/package.json +++ b/packages/coverage-istanbul/package.json @@ -56,6 +56,6 @@ "@types/istanbul-lib-report": "^3.0.0", "@types/istanbul-lib-source-maps": "^4.0.1", "@types/istanbul-reports": "^3.0.1", - "pathe": "^0.2.0" + "pathe": "^1.1.0" } } diff --git a/packages/expect/package.json b/packages/expect/package.json index 01e1dd120979..ed348b0f1c7d 100644 --- a/packages/expect/package.json +++ b/packages/expect/package.json @@ -31,7 +31,9 @@ "dependencies": { "@vitest/spy": "workspace:*", "@vitest/utils": "workspace:*", - "chai": "^4.3.7", + "chai": "^4.3.7" + }, + "devDependencies": { "picocolors": "^1.0.0" } } diff --git a/packages/expect/rollup.config.js b/packages/expect/rollup.config.js index fddfda5974ce..dbaa1b71eb00 100644 --- a/packages/expect/rollup.config.js +++ b/packages/expect/rollup.config.js @@ -8,6 +8,7 @@ const external = [ ...builtinModules, ...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {}), + '@vitest/utils/diff', ] const plugins = [ diff --git a/packages/expect/src/index.ts b/packages/expect/src/index.ts index e472669a1e1e..f57106312f83 100644 --- a/packages/expect/src/index.ts +++ b/packages/expect/src/index.ts @@ -5,3 +5,4 @@ export * from './types' export { getState, setState } from './state' export { JestChaiExpect } from './jest-expect' export { JestExtend } from './jest-extend' +export { setColors as setupColors } from '@vitest/utils' diff --git a/packages/expect/src/jest-expect.ts b/packages/expect/src/jest-expect.ts index e57f6e9c574f..30f135865574 100644 --- a/packages/expect/src/jest-expect.ts +++ b/packages/expect/src/jest-expect.ts @@ -1,17 +1,18 @@ -import c from 'picocolors' import { AssertionError } from 'chai' -import { assertTypes, unifiedDiff } from '@vitest/utils' +import { assertTypes, getColors } from '@vitest/utils' import type { Constructable } from '@vitest/utils' import type { EnhancedSpy } from '@vitest/spy' import { isMockFunction } from '@vitest/spy' import type { ChaiPlugin } from './types' import { arrayBufferEquality, generateToBeMessage, iterableEquality, equals as jestEquals, sparseArrayEquality, subsetEquality, typeEquality } from './jest-utils' import type { AsymmetricMatcher } from './jest-asymmetric-matchers' -import { stringify } from './jest-matcher-utils' +import { diff, stringify } from './jest-matcher-utils' import { JEST_MATCHERS_OBJECT } from './constants' // Jest Expect Compact export const JestChaiExpect: ChaiPlugin = (chai, utils) => { + const c = () => getColors() + function def(name: keyof Vi.Assertion | (keyof Vi.Assertion)[], fn: ((this: Chai.AssertionStatic & Vi.Assertion, ...args: any[]) => any)) { const addMethod = (n: keyof Vi.Assertion) => { utils.addMethod(chai.Assertion.prototype, n, fn) @@ -351,31 +352,31 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { return `${i}th` } const formatCalls = (spy: EnhancedSpy, msg: string, actualCall?: any) => { - msg += c.gray(`\n\nReceived: \n${spy.mock.calls.map((callArg, i) => { - let methodCall = c.bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call:\n\n`) + msg += c().gray(`\n\nReceived: \n${spy.mock.calls.map((callArg, i) => { + let methodCall = c().bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call:\n\n`) if (actualCall) - methodCall += unifiedDiff(stringify(callArg), stringify(actualCall), { showLegend: false }) + methodCall += diff(callArg, actualCall, { showLegend: false }) else methodCall += stringify(callArg).split('\n').map(line => ` ${line}`).join('\n') methodCall += '\n' return methodCall }).join('\n')}`) - msg += c.gray(`\n\nNumber of calls: ${c.bold(spy.mock.calls.length)}\n`) + msg += c().gray(`\n\nNumber of calls: ${c().bold(spy.mock.calls.length)}\n`) return msg } const formatReturns = (spy: EnhancedSpy, msg: string, actualReturn?: any) => { - msg += c.gray(`\n\nReceived: \n${spy.mock.results.map((callReturn, i) => { - let methodCall = c.bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call return:\n\n`) + msg += c().gray(`\n\nReceived: \n${spy.mock.results.map((callReturn, i) => { + let methodCall = c().bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call return:\n\n`) if (actualReturn) - methodCall += unifiedDiff(stringify(callReturn.value), stringify(actualReturn), { showLegend: false }) + methodCall += diff(callReturn.value, actualReturn, { showLegend: false }) else methodCall += stringify(callReturn).split('\n').map(line => ` ${line}`).join('\n') methodCall += '\n' return methodCall }).join('\n')}`) - msg += c.gray(`\n\nNumber of calls: ${c.bold(spy.mock.calls.length)}\n`) + msg += c().gray(`\n\nNumber of calls: ${c().bold(spy.mock.calls.length)}\n`) return msg } def(['toHaveBeenCalledTimes', 'toBeCalledTimes'], function (number: number) { diff --git a/packages/expect/src/jest-extend.ts b/packages/expect/src/jest-extend.ts index a7d9795668ff..5651b578f1cf 100644 --- a/packages/expect/src/jest-extend.ts +++ b/packages/expect/src/jest-extend.ts @@ -9,7 +9,7 @@ import { JEST_MATCHERS_OBJECT } from './constants' import { AsymmetricMatcher } from './jest-asymmetric-matchers' import { getState } from './state' -import * as matcherUtils from './jest-matcher-utils' +import { diff, getMatcherUtils, stringify } from './jest-matcher-utils' import { equals, @@ -22,7 +22,9 @@ const getMatcherState = (assertion: Chai.AssertionStatic & Chai.Assertion, expec const isNot = util.flag(assertion, 'negate') as boolean const promise = util.flag(assertion, 'promise') || '' const jestUtils = { - ...matcherUtils, + ...getMatcherUtils(), + diff, + stringify, iterableEquality, subsetEquality, } diff --git a/packages/expect/src/jest-matcher-utils.ts b/packages/expect/src/jest-matcher-utils.ts index 21902c5c2d0b..c71c3302b111 100644 --- a/packages/expect/src/jest-matcher-utils.ts +++ b/packages/expect/src/jest-matcher-utils.ts @@ -1,93 +1,114 @@ -import c from 'picocolors' -import { stringify, unifiedDiff } from '@vitest/utils' +import { getColors, stringify } from '@vitest/utils' +import { unifiedDiff } from '@vitest/utils/diff' import type { DiffOptions, MatcherHintOptions } from './types' export { stringify } -export const EXPECTED_COLOR = c.green -export const RECEIVED_COLOR = c.red -export const INVERTED_COLOR = c.inverse -export const BOLD_WEIGHT = c.bold -export const DIM_COLOR = c.dim - -export function matcherHint( - matcherName: string, - received = 'received', - expected = 'expected', - options: MatcherHintOptions = {}, -) { - const { - comment = '', - isDirectExpectCall = false, // seems redundant with received === '' - isNot = false, - promise = '', - secondArgument = '', - expectedColor = EXPECTED_COLOR, - receivedColor = RECEIVED_COLOR, - secondArgumentColor = EXPECTED_COLOR, - } = options - let hint = '' - let dimString = 'expect' // concatenate adjacent dim substrings - - if (!isDirectExpectCall && received !== '') { - hint += DIM_COLOR(`${dimString}(`) + receivedColor(received) - dimString = ')' - } +export function getMatcherUtils() { + const c = () => getColors() - if (promise !== '') { - hint += DIM_COLOR(`${dimString}.`) + promise - dimString = '' - } + const EXPECTED_COLOR = c().green + const RECEIVED_COLOR = c().red + const INVERTED_COLOR = c().inverse + const BOLD_WEIGHT = c().bold + const DIM_COLOR = c().dim - if (isNot) { - hint += `${DIM_COLOR(`${dimString}.`)}not` - dimString = '' - } + function matcherHint( + matcherName: string, + received = 'received', + expected = 'expected', + options: MatcherHintOptions = {}, + ) { + const { + comment = '', + isDirectExpectCall = false, // seems redundant with received === '' + isNot = false, + promise = '', + secondArgument = '', + expectedColor = EXPECTED_COLOR, + receivedColor = RECEIVED_COLOR, + secondArgumentColor = EXPECTED_COLOR, + } = options + let hint = '' + let dimString = 'expect' // concatenate adjacent dim substrings + + if (!isDirectExpectCall && received !== '') { + hint += DIM_COLOR(`${dimString}(`) + receivedColor(received) + dimString = ')' + } - if (matcherName.includes('.')) { + if (promise !== '') { + hint += DIM_COLOR(`${dimString}.`) + promise + dimString = '' + } + + if (isNot) { + hint += `${DIM_COLOR(`${dimString}.`)}not` + dimString = '' + } + + if (matcherName.includes('.')) { // Old format: for backward compatibility, // especially without promise or isNot options - dimString += matcherName - } - else { + dimString += matcherName + } + else { // New format: omit period from matcherName arg - hint += DIM_COLOR(`${dimString}.`) + matcherName - dimString = '' - } + hint += DIM_COLOR(`${dimString}.`) + matcherName + dimString = '' + } - if (expected === '') { - dimString += '()' - } - else { - hint += DIM_COLOR(`${dimString}(`) + expectedColor(expected) - if (secondArgument) - hint += DIM_COLOR(', ') + secondArgumentColor(secondArgument) - dimString = ')' - } + if (expected === '') { + dimString += '()' + } + else { + hint += DIM_COLOR(`${dimString}(`) + expectedColor(expected) + if (secondArgument) + hint += DIM_COLOR(', ') + secondArgumentColor(secondArgument) + dimString = ')' + } - if (comment !== '') - dimString += ` // ${comment}` + if (comment !== '') + dimString += ` // ${comment}` - if (dimString !== '') - hint += DIM_COLOR(dimString) + if (dimString !== '') + hint += DIM_COLOR(dimString) - return hint -} + return hint + } -const SPACE_SYMBOL = '\u{00B7}' // middle dot + const SPACE_SYMBOL = '\u{00B7}' // middle dot -// Instead of inverse highlight which now implies a change, -// replace common spaces with middle dot at the end of any line. -const replaceTrailingSpaces = (text: string): string => - text.replace(/\s+$/gm, spaces => SPACE_SYMBOL.repeat(spaces.length)) + // Instead of inverse highlight which now implies a change, + // replace common spaces with middle dot at the end of any line. + const replaceTrailingSpaces = (text: string): string => + text.replace(/\s+$/gm, spaces => SPACE_SYMBOL.repeat(spaces.length)) -export const printReceived = (object: unknown): string => - RECEIVED_COLOR(replaceTrailingSpaces(stringify(object))) -export const printExpected = (value: unknown): string => - EXPECTED_COLOR(replaceTrailingSpaces(stringify(value))) + const printReceived = (object: unknown): string => + RECEIVED_COLOR(replaceTrailingSpaces(stringify(object))) + const printExpected = (value: unknown): string => + EXPECTED_COLOR(replaceTrailingSpaces(stringify(value))) + + return { + EXPECTED_COLOR, + RECEIVED_COLOR, + INVERTED_COLOR, + BOLD_WEIGHT, + DIM_COLOR, + + matcherHint, + printReceived, + printExpected, + } +} // TODO: do something with options -// eslint-disable-next-line @typescript-eslint/no-unused-vars export function diff(a: any, b: any, options?: DiffOptions) { - return unifiedDiff(stringify(b), stringify(a)) + const c = getColors() + return unifiedDiff(stringify(b), stringify(a), { + colorDim: c.dim, + colorSuccess: c.green, + colorError: c.red, + showLegend: options?.showLegend, + }) } diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index 3ff834b17753..1b6ae780b757 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -9,7 +9,7 @@ import type { use as chaiUse } from 'chai' */ import type { Formatter } from 'picocolors/types' -import type * as jestMatcherUtils from './jest-matcher-utils' +import type { diff, getMatcherUtils, stringify } from './jest-matcher-utils' export type FirstFunctionArgument = T extends (arg: infer A) => unknown ? A : never export type ChaiPlugin = FirstFunctionArgument @@ -47,6 +47,7 @@ export interface DiffOptions { patchColor?: Formatter // pretty-format type compareKeys?: any + showLegend?: boolean } export interface MatcherState { @@ -71,7 +72,9 @@ export interface MatcherState { // snapshotState: SnapshotState suppressedErrors: Array testPath?: string - utils: typeof jestMatcherUtils & { + utils: ReturnType & { + diff: typeof diff + stringify: typeof stringify iterableEquality: Tester subsetEquality: Tester } diff --git a/packages/runner/README.md b/packages/runner/README.md new file mode 100644 index 000000000000..2796b6aacd03 --- /dev/null +++ b/packages/runner/README.md @@ -0,0 +1,5 @@ +# @vitest/runner + +Vitest mechanism to collect and run tasks. + +[GitHub](https://github.com/vitest-dev/vitest) | [Documentation](https://vitest.dev/advanced/runner) diff --git a/packages/runner/package.json b/packages/runner/package.json new file mode 100644 index 000000000000..ea277ed7877d --- /dev/null +++ b/packages/runner/package.json @@ -0,0 +1,45 @@ +{ + "name": "@vitest/runner", + "type": "module", + "version": "0.27.2", + "description": "Vitest test runner", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/vitest-dev/vitest.git", + "directory": "packages/runner" + }, + "sideEffects": true, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./utils": { + "types": "./dist/utils.d.ts", + "import": "./dist/utils.js" + }, + "./types": { + "types": "./dist/types.d.ts", + "import": "./dist/types.js" + }, + "./*": "./*" + }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist", + "*.d.ts" + ], + "scripts": { + "build": "rimraf dist && rollup -c", + "dev": "rollup -c --watch", + "prepublishOnly": "pnpm build" + }, + "dependencies": { + "@vitest/utils": "workspace:*", + "p-limit": "^4.0.0", + "pathe": "^1.1.0" + } +} diff --git a/packages/runner/rollup.config.js b/packages/runner/rollup.config.js new file mode 100644 index 000000000000..a8c6d7cff5d2 --- /dev/null +++ b/packages/runner/rollup.config.js @@ -0,0 +1,57 @@ +import { builtinModules } from 'module' +import esbuild from 'rollup-plugin-esbuild' +import dts from 'rollup-plugin-dts' +import { defineConfig } from 'rollup' +import pkg from './package.json' + +const external = [ + ...builtinModules, + ...Object.keys(pkg.dependencies || {}), + ...Object.keys(pkg.peerDependencies || {}), +] + +const entries = { + index: 'src/index.ts', + utils: 'src/utils/index.ts', + types: 'src/types/index.ts', +} + +const plugins = [ + esbuild({ + target: 'node14', + }), +] + +export default defineConfig([ + { + input: entries, + output: { + dir: 'dist', + format: 'esm', + entryFileNames: '[name].js', + chunkFileNames: 'chunk-[name].js', + }, + external, + plugins, + onwarn, + }, + { + input: entries, + output: { + dir: 'dist', + entryFileNames: '[name].d.ts', + format: 'esm', + }, + external, + plugins: [ + dts({ respectExternal: true }), + ], + onwarn, + }, +]) + +function onwarn(message) { + if (['EMPTY_BUNDLE', 'CIRCULAR_DEPENDENCY'].includes(message.code)) + return + console.error(message) +} diff --git a/packages/vitest/src/runtime/collect.ts b/packages/runner/src/collect.ts similarity index 57% rename from packages/vitest/src/runtime/collect.ts rename to packages/runner/src/collect.ts index 7d15461f951a..e363fe319fe0 100644 --- a/packages/vitest/src/runtime/collect.ts +++ b/packages/runner/src/collect.ts @@ -1,29 +1,22 @@ -import type { File, ResolvedConfig } from '../types' -import { getWorkerState, isBrowser, relativePath } from '../utils' -import { calculateSuiteHash, generateHash, interpretTaskModes, someTasksAreOnly } from '../utils/collect' -import { clearCollectorContext, defaultSuite } from './suite' +import { relative } from 'pathe' +import type { File } from './types' +import type { VitestRunner } from './types/runner' +import { calculateSuiteHash, generateHash, interpretTaskModes, someTasksAreOnly } from './utils/collect' +import { clearCollectorContext, getDefaultSuite } from './suite' import { getHooks, setHooks } from './map' -import { processError } from './error' +import { processError } from './utils/error' import { collectorContext } from './context' import { runSetupFiles } from './setup' const now = Date.now -export async function collectTests(paths: string[], config: ResolvedConfig): Promise { +export async function collectTests(paths: string[], runner: VitestRunner): Promise { const files: File[] = [] - const browserHashMap = getWorkerState().browserHashMap! - async function importFromBrowser(filepath: string) { - const match = filepath.match(/^(\w:\/)/) - const hash = browserHashMap.get(filepath) - if (match) - return await import(`/@fs/${filepath.slice(match[1].length)}?v=${hash}`) - else - return await import(`${filepath}?v=${hash}`) - } + const config = runner.config for (const filepath of paths) { - const path = relativePath(config.root, filepath) + const path = relative(config.root, filepath) const file: File = { id: generateHash(path), name: path, @@ -34,20 +27,18 @@ export async function collectTests(paths: string[], config: ResolvedConfig): Pro projectName: config.name, } - clearCollectorContext() + clearCollectorContext(runner) try { const setupStart = now() - await runSetupFiles(config) + await runSetupFiles(config, runner) const collectStart = now() file.setupDuration = collectStart - setupStart - if (config.browser && isBrowser) - await importFromBrowser(filepath) - else - await import(filepath) - const defaultTasks = await defaultSuite.collect(file) + await runner.importFile(filepath, 'collect') + + const defaultTasks = await getDefaultSuite().collect(file) setHooks(file, getHooks(defaultTasks)) @@ -55,7 +46,7 @@ export async function collectTests(paths: string[], config: ResolvedConfig): Pro if (c.type === 'test') { file.tasks.push(c) } - else if (c.type === 'benchmark') { + else if (c.type === 'custom') { file.tasks.push(c) } else if (c.type === 'suite') { @@ -76,8 +67,6 @@ export async function collectTests(paths: string[], config: ResolvedConfig): Pro error, errors: [error], } - if (config.browser) - console.error(e) } calculateSuiteHash(file) diff --git a/packages/vitest/src/runtime/context.ts b/packages/runner/src/context.ts similarity index 65% rename from packages/vitest/src/runtime/context.ts rename to packages/runner/src/context.ts index 2f1fe95f6c8c..40c6706f4d21 100644 --- a/packages/vitest/src/runtime/context.ts +++ b/packages/runner/src/context.ts @@ -1,6 +1,7 @@ -import type { Awaitable, RuntimeContext, SuiteCollector, Test, TestContext } from '../types' -import { createExpect } from '../integrations/chai' -import { clearTimeout, getWorkerState, setTimeout } from '../utils' +import type { Awaitable } from '@vitest/utils' +import { getSafeTimers } from '@vitest/utils' +import type { RuntimeContext, SuiteCollector, Test, TestContext } from './types' +import type { VitestRunner } from './types/runner' export const collectorContext: RuntimeContext = { tasks: [], @@ -18,22 +19,16 @@ export async function runWithSuite(suite: SuiteCollector, fn: (() => Awaitable any)>( fn: T, - timeout = getDefaultTestTimeout(), + timeout: number, isHook = false, ): T { if (timeout <= 0 || timeout === Infinity) return fn + const { setTimeout, clearTimeout } = getSafeTimers() + return ((...args: (T extends ((...args: infer A) => any) ? A : never)) => { return Promise.race([fn(...args), new Promise((resolve, reject) => { const timer = setTimeout(() => { @@ -46,32 +41,19 @@ export function withTimeout any)>( }) as T } -export function createTestContext(test: Test): TestContext { +export function createTestContext(test: Test, runner: VitestRunner): TestContext { const context = function () { throw new Error('done() callback is deprecated, use promise instead') } as unknown as TestContext context.meta = test - let _expect: Vi.ExpectStatic | undefined - Object.defineProperty(context, 'expect', { - get() { - if (!_expect) - _expect = createExpect(test) - return _expect - }, - }) - Object.defineProperty(context, '_local', { - get() { - return _expect != null - }, - }) context.onTestFailed = (fn) => { test.onFailed ||= [] test.onFailed.push(fn) } - return context + return runner.extendTestContext?.(context) || context } function makeTimeoutMsg(isHook: boolean, timeout: number) { diff --git a/packages/vitest/src/runtime/hooks.ts b/packages/runner/src/hooks.ts similarity index 84% rename from packages/vitest/src/runtime/hooks.ts rename to packages/runner/src/hooks.ts index ed391e109aa2..4ff8f4a95785 100644 --- a/packages/vitest/src/runtime/hooks.ts +++ b/packages/runner/src/hooks.ts @@ -1,7 +1,9 @@ -import type { OnTestFailedHandler, SuiteHooks, Test } from '../types' -import { getDefaultHookTimeout, withTimeout } from './context' -import { getCurrentSuite } from './suite' +import type { OnTestFailedHandler, SuiteHooks, Test } from './types' +import { getCurrentSuite, getRunner } from './suite' import { getCurrentTest } from './test-state' +import { withTimeout } from './context' + +const getDefaultHookTimeout = () => getRunner().config.hookTimeout // suite hooks export const beforeAll = (fn: SuiteHooks['beforeAll'][0], timeout?: number) => getCurrentSuite().on('beforeAll', withTimeout(fn, timeout ?? getDefaultHookTimeout(), true)) diff --git a/packages/runner/src/index.ts b/packages/runner/src/index.ts new file mode 100644 index 000000000000..8860e67d0e9c --- /dev/null +++ b/packages/runner/src/index.ts @@ -0,0 +1,5 @@ +export { startTests, updateTask } from './run' +export { test, it, describe, suite, getCurrentSuite } from './suite' +export { beforeAll, beforeEach, afterAll, afterEach, onTestFailed } from './hooks' +export { setFn, getFn } from './map' +export * from './types' diff --git a/packages/runner/src/map.ts b/packages/runner/src/map.ts new file mode 100644 index 000000000000..6cf99cbd4643 --- /dev/null +++ b/packages/runner/src/map.ts @@ -0,0 +1,22 @@ +import type { Awaitable } from '@vitest/utils' +import type { Suite, SuiteHooks, Test } from './types' + +// use WeakMap here to make the Test and Suite object serializable +const fnMap = new WeakMap() +const hooksMap = new WeakMap() + +export function setFn(key: Test, fn: (() => Awaitable)) { + fnMap.set(key, fn) +} + +export function getFn(key: Task): (() => Awaitable) { + return fnMap.get(key as any) +} + +export function setHooks(key: Suite, hooks: SuiteHooks) { + hooksMap.set(key, hooks) +} + +export function getHooks(key: Suite): SuiteHooks { + return hooksMap.get(key) +} diff --git a/packages/runner/src/run.ts b/packages/runner/src/run.ts new file mode 100644 index 000000000000..acca6bd8f6e4 --- /dev/null +++ b/packages/runner/src/run.ts @@ -0,0 +1,339 @@ +import limit from 'p-limit' +import { getSafeTimers, shuffle } from '@vitest/utils' +import type { VitestRunner } from './types/runner' +import type { File, HookCleanupCallback, HookListener, SequenceHooks, Suite, SuiteHooks, Task, TaskResult, TaskState, Test } from './types' +import { partitionSuiteChildren } from './utils/suite' +import { getFn, getHooks } from './map' +import { collectTests } from './collect' +import { processError } from './utils/error' +import { setCurrentTest } from './test-state' +import { hasFailed, hasTests } from './utils/tasks' + +const now = Date.now + +function updateSuiteHookState(suite: Task, name: keyof SuiteHooks, state: TaskState, runner: VitestRunner) { + if (!suite.result) + suite.result = { state: 'run' } + if (!suite.result?.hooks) + suite.result.hooks = {} + const suiteHooks = suite.result.hooks + if (suiteHooks) { + suiteHooks[name] = state + updateTask(suite, runner) + } +} + +function getSuiteHooks(suite: Suite, name: keyof SuiteHooks, sequence: SequenceHooks) { + const hooks = getHooks(suite)[name] + if (sequence === 'stack' && (name === 'afterAll' || name === 'afterEach')) + return hooks.slice().reverse() + return hooks +} + +export async function callSuiteHook( + suite: Suite, + currentTask: Task, + name: T, + runner: VitestRunner, + args: SuiteHooks[T][0] extends HookListener ? A : never, +): Promise { + const sequence = runner.config.sequence.hooks + + const callbacks: HookCleanupCallback[] = [] + if (name === 'beforeEach' && suite.suite) { + callbacks.push( + ...await callSuiteHook(suite.suite, currentTask, name, runner, args), + ) + } + + updateSuiteHookState(currentTask, name, 'run', runner) + + const hooks = getSuiteHooks(suite, name, sequence) + + if (sequence === 'parallel') { + callbacks.push(...await Promise.all(hooks.map(fn => fn(...args as any)))) + } + else { + for (const hook of hooks) + callbacks.push(await hook(...args as any)) + } + + updateSuiteHookState(currentTask, name, 'pass', runner) + + if (name === 'afterEach' && suite.suite) { + callbacks.push( + ...await callSuiteHook(suite.suite, currentTask, name, runner, args), + ) + } + + return callbacks +} + +const packs = new Map() +let updateTimer: any +let previousUpdate: Promise | undefined + +export function updateTask(task: Task, runner: VitestRunner) { + packs.set(task.id, task.result) + + const { clearTimeout, setTimeout } = getSafeTimers() + + clearTimeout(updateTimer) + updateTimer = setTimeout(() => { + previousUpdate = sendTasksUpdate(runner) + }, 10) +} + +async function sendTasksUpdate(runner: VitestRunner) { + const { clearTimeout } = getSafeTimers() + clearTimeout(updateTimer) + await previousUpdate + + if (packs.size) { + const p = runner.onTaskUpdate?.(Array.from(packs)) + packs.clear() + return p + } +} + +const callCleanupHooks = async (cleanups: HookCleanupCallback[]) => { + await Promise.all(cleanups.map(async (fn) => { + if (typeof fn !== 'function') + return + await fn() + })) +} + +export async function runTest(test: Test, runner: VitestRunner) { + await runner.onBeforeRunTest?.(test) + + if (test.mode !== 'run') + return + + if (test.result?.state === 'fail') { + updateTask(test, runner) + return + } + + const start = now() + + test.result = { + state: 'run', + startTime: start, + } + updateTask(test, runner) + + setCurrentTest(test) + + const retry = test.retry || 1 + for (let retryCount = 0; retryCount < retry; retryCount++) { + let beforeEachCleanups: HookCleanupCallback[] = [] + try { + await runner.onBeforeTryTest?.(test, retryCount) + + beforeEachCleanups = await callSuiteHook(test.suite, test, 'beforeEach', runner, [test.context, test.suite]) + + test.result.retryCount = retryCount + + if (runner.runTest) { + await runner.runTest(test) + } + else { + const fn = getFn(test) + if (!fn) + throw new Error('Test function is not found. Did you add it using `setFn`?') + await fn() + } + + await runner.onAfterTryTest?.(test, retryCount) + + test.result.state = 'pass' + } + catch (e) { + const error = processError(e) + test.result.state = 'fail' + test.result.error = error + test.result.errors = [error] + } + + try { + await callSuiteHook(test.suite, test, 'afterEach', runner, [test.context, test.suite]) + await callCleanupHooks(beforeEachCleanups) + } + catch (e) { + const error = processError(e) + test.result.state = 'fail' + test.result.error = error + test.result.errors = [error] + } + + if (test.result.state === 'pass') + break + + // update retry info + updateTask(test, runner) + } + + if (test.result.state === 'fail') + await Promise.all(test.onFailed?.map(fn => fn(test.result!)) || []) + + // if test is marked to be failed, flip the result + if (test.fails) { + if (test.result.state === 'pass') { + const error = processError(new Error('Expect test to fail')) + test.result.state = 'fail' + test.result.error = error + test.result.errors = [error] + } + else { + test.result.state = 'pass' + test.result.error = undefined + test.result.errors = undefined + } + } + + setCurrentTest(undefined) + + test.result.duration = now() - start + + await runner.onAfterRunTest?.(test) + + updateTask(test, runner) +} + +function markTasksAsSkipped(suite: Suite, runner: VitestRunner) { + suite.tasks.forEach((t) => { + t.mode = 'skip' + t.result = { ...t.result, state: 'skip' } + updateTask(t, runner) + if (t.type === 'suite') + markTasksAsSkipped(t, runner) + }) +} + +export async function runSuite(suite: Suite, runner: VitestRunner) { + await runner.onBeforeRunSuite?.(suite) + + if (suite.result?.state === 'fail') { + markTasksAsSkipped(suite, runner) + updateTask(suite, runner) + return + } + + const start = now() + + suite.result = { + state: 'run', + startTime: start, + } + + updateTask(suite, runner) + + if (suite.mode === 'skip') { + suite.result.state = 'skip' + } + else if (suite.mode === 'todo') { + suite.result.state = 'todo' + } + else { + try { + const beforeAllCleanups = await callSuiteHook(suite, suite, 'beforeAll', runner, [suite]) + + if (runner.runSuite) { + await runner.runSuite(suite) + } + else { + for (let tasksGroup of partitionSuiteChildren(suite)) { + if (tasksGroup[0].concurrent === true) { + const mutex = limit(runner.config.maxConcurrency) + await Promise.all(tasksGroup.map(c => mutex(() => runSuiteChild(c, runner)))) + } + else { + const { sequence } = runner.config + if (sequence.shuffle || suite.shuffle) { + // run describe block independently from tests + const suites = tasksGroup.filter(group => group.type === 'suite') + const tests = tasksGroup.filter(group => group.type === 'test') + const groups = shuffle([suites, tests], sequence.seed) + tasksGroup = groups.flatMap(group => shuffle(group, sequence.seed)) + } + for (const c of tasksGroup) + await runSuiteChild(c, runner) + } + } + } + + await callSuiteHook(suite, suite, 'afterAll', runner, [suite]) + await callCleanupHooks(beforeAllCleanups) + } + catch (e) { + const error = processError(e) + suite.result.state = 'fail' + suite.result.error = error + suite.result.errors = [error] + } + } + suite.result.duration = now() - start + + if (suite.mode === 'run') { + if (!hasTests(suite)) { + suite.result.state = 'fail' + if (!suite.result.error) { + const error = processError(new Error(`No test found in suite ${suite.name}`)) + suite.result.error = error + suite.result.errors = [error] + } + } + else if (hasFailed(suite)) { + suite.result.state = 'fail' + } + else { + suite.result.state = 'pass' + } + } + + await runner.onAfterRunSuite?.(suite) + + updateTask(suite, runner) +} + +async function runSuiteChild(c: Task, runner: VitestRunner) { + if (c.type === 'test') + return runTest(c, runner) + + else if (c.type === 'suite') + return runSuite(c, runner) +} + +export async function runFiles(files: File[], runner: VitestRunner) { + for (const file of files) { + if (!file.tasks.length && !runner.config.passWithNoTests) { + if (!file.result?.errors?.length) { + const error = processError(new Error(`No test suite found in file ${file.filepath}`)) + file.result = { + state: 'fail', + error, + errors: [error], + } + } + } + await runSuite(file, runner) + } +} + +export async function startTests(paths: string[], runner: VitestRunner) { + await runner.onBeforeCollect?.(paths) + + const files = await collectTests(paths, runner) + + runner.onCollected?.(files) + await runner.onBeforeRun?.(files) + + await runFiles(files, runner) + + await runner.onAfterRun?.(files) + + await sendTasksUpdate(runner) + + return files +} diff --git a/packages/runner/src/setup.ts b/packages/runner/src/setup.ts new file mode 100644 index 000000000000..750a57a18c4d --- /dev/null +++ b/packages/runner/src/setup.ts @@ -0,0 +1,11 @@ +import { toArray } from '@vitest/utils' +import type { VitestRunner, VitestRunnerConfig } from './types' + +export async function runSetupFiles(config: VitestRunnerConfig, runner: VitestRunner) { + const files = toArray(config.setupFiles) + await Promise.all( + files.map(async (fsPath) => { + await runner.importFile(fsPath, 'setup') + }), + ) +} diff --git a/packages/vitest/src/runtime/suite.ts b/packages/runner/src/suite.ts similarity index 73% rename from packages/vitest/src/runtime/suite.ts rename to packages/runner/src/suite.ts index 8a69f6bc05e1..11a25d13c7f9 100644 --- a/packages/vitest/src/runtime/suite.ts +++ b/packages/runner/src/suite.ts @@ -1,10 +1,9 @@ -import util from 'util' -import { util as chaiUtil } from 'chai' -import type { BenchFunction, BenchOptions, Benchmark, BenchmarkAPI, File, RunMode, Suite, SuiteAPI, SuiteCollector, SuiteFactory, SuiteHooks, Task, Test, TestAPI, TestFunction, TestOptions } from '../types' -import { getWorkerState, isObject, isRunningInBenchmark, isRunningInTest, noop, objectAttr } from '../utils' -import { createChainable } from './chain' +import { format, isObject, noop, objDisplay, objectAttr } from '@vitest/utils' +import type { File, RunMode, Suite, SuiteAPI, SuiteCollector, SuiteFactory, SuiteHooks, Task, TaskCustom, Test, TestAPI, TestFunction, TestOptions } from './types' +import type { VitestRunner } from './types/runner' +import { createChainable } from './utils/chain' import { collectTask, collectorContext, createTestContext, runWithSuite, withTimeout } from './context' -import { getHooks, setBenchOptions, setFn, setHooks } from './map' +import { getHooks, setFn, setHooks } from './map' // apis export const suite = createSuite() @@ -13,23 +12,26 @@ export const test = createTest( getCurrentSuite().test.fn.call(this, name, fn, options) }, ) -export const bench = createBenchmark( - function (name, fn: BenchFunction = noop, options: BenchOptions = {}) { - getCurrentSuite().benchmark.fn.call(this, name, fn, options) - }, -) // alias export const describe = suite export const it = test -const workerState = getWorkerState() +let runner: VitestRunner +let defaultSuite: SuiteCollector + +export function getDefaultSuite() { + return defaultSuite +} -export const defaultSuite = workerState.config.sequence.shuffle - ? suite.shuffle('') - : suite('') +export function getRunner() { + return runner +} -export function clearCollectorContext() { +export function clearCollectorContext(currentRunner: VitestRunner) { + if (!defaultSuite) + defaultSuite = currentRunner.config.sequence.shuffle ? suite.shuffle('') : suite('') + runner = currentRunner collectorContext.tasks.length = 0 defaultSuite.clear() collectorContext.currentSuite = defaultSuite @@ -50,7 +52,7 @@ export function createSuiteHooks() { // implementations function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, mode: RunMode, concurrent?: boolean, shuffle?: boolean, suiteOptions?: number | TestOptions) { - const tasks: (Benchmark | Test | Suite | SuiteCollector)[] = [] + const tasks: (Test | TaskCustom | Suite | SuiteCollector)[] = [] const factoryQueue: (Test | Suite | SuiteCollector)[] = [] let suite: Suite @@ -58,9 +60,6 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m initSuite() const test = createTest(function (name: string, fn = noop, options = suiteOptions) { - if (!isRunningInTest()) - throw new Error('`test()` and `it()` is only available in test mode.') - const mode = this.only ? 'only' : this.skip ? 'skip' : this.todo ? 'todo' : 'run' if (typeof options === 'number') @@ -81,7 +80,7 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m if (shuffle) test.shuffle = true - const context = createTestContext(test) + const context = createTestContext(test, runner) // create test context Object.defineProperty(test, 'context', { value: context, @@ -90,30 +89,23 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m setFn(test, withTimeout( () => fn(context), - options?.timeout, + options?.timeout ?? runner.config.testTimeout, )) tasks.push(test) }) - const benchmark = createBenchmark(function (name: string, fn = noop, options: BenchOptions = {}) { - const mode = this.only ? 'only' : this.skip ? 'skip' : this.todo ? 'todo' : 'run' - - if (!isRunningInBenchmark()) - throw new Error('`bench()` is only available in benchmark mode. Run with `vitest bench` instead.') - - const benchmark: Benchmark = { - type: 'benchmark', + const custom = function (this: Record, name = '') { + const self = this || {} + const task: TaskCustom = { id: '', name, - mode, - suite: undefined!, + type: 'custom', + mode: self.only ? 'only' : self.skip ? 'skip' : self.todo ? 'todo' : 'run', } - - setFn(benchmark, fn) - setBenchOptions(benchmark, options) - tasks.push(benchmark) - }) + tasks.push(task) + return task + } const collector: SuiteCollector = { type: 'collector', @@ -121,8 +113,8 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m mode, test, tasks, - benchmark, collect, + custom, clear, on: addHook, } @@ -244,25 +236,6 @@ function createTest(fn: ( ) as TestAPI } -function createBenchmark(fn: ( - ( - this: Record<'skip' | 'only' | 'todo', boolean | undefined>, - name: string, - fn?: BenchFunction, - options?: BenchOptions - ) => void -)) { - const benchmark = createChainable( - ['skip', 'only', 'todo'], - fn, - ) as BenchmarkAPI - - benchmark.skipIf = (condition: any) => (condition ? benchmark.skip : benchmark) as BenchmarkAPI - benchmark.runIf = (condition: any) => (condition ? benchmark : benchmark.skip) as BenchmarkAPI - - return benchmark as BenchmarkAPI -} - function formatTitle(template: string, items: any[], idx: number) { if (template.includes('%#')) { // '%#' match index of the test case @@ -272,10 +245,10 @@ function formatTitle(template: string, items: any[], idx: number) { .replace(/__vitest_escaped_%__/g, '%%') } const count = template.split('%').length - 1 - let formatted = util.format(template, ...items.slice(0, count)) + let formatted = format(template, ...items.slice(0, count)) if (isObject(items[0])) { formatted = formatted.replace(/\$([$\w_.]+)/g, - (_, key) => chaiUtil.objDisplay(objectAttr(items[0], key)) as unknown as string, + (_, key) => objDisplay(objectAttr(items[0], key)) as unknown as string, // https://github.com/chaijs/chai/pull/1490 ) } diff --git a/packages/vitest/src/runtime/test-state.ts b/packages/runner/src/test-state.ts similarity index 80% rename from packages/vitest/src/runtime/test-state.ts rename to packages/runner/src/test-state.ts index 26c81cf024f8..1761e8130a96 100644 --- a/packages/vitest/src/runtime/test-state.ts +++ b/packages/runner/src/test-state.ts @@ -1,4 +1,4 @@ -import type { Test } from '../types' +import type { Test } from './types' let _test: Test | undefined diff --git a/packages/runner/src/types/index.ts b/packages/runner/src/types/index.ts new file mode 100644 index 000000000000..01ac112a8a57 --- /dev/null +++ b/packages/runner/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './tasks' +export * from './runner' diff --git a/packages/runner/src/types/runner.ts b/packages/runner/src/types/runner.ts new file mode 100644 index 000000000000..35c228204d9c --- /dev/null +++ b/packages/runner/src/types/runner.ts @@ -0,0 +1,99 @@ +import type { File, SequenceHooks, Suite, TaskResult, Test, TestContext } from './tasks' + +export interface VitestRunnerConfig { + root: string + setupFiles: string[] | string + name: string + passWithNoTests: boolean + testNamePattern?: RegExp + allowOnly?: boolean + sequence: { + shuffle?: boolean + seed?: number + hooks: SequenceHooks + } + maxConcurrency: number + testTimeout: number + hookTimeout: number +} + +export type VitestRunnerImportSource = 'collect' | 'setup' + +export interface VitestRunnerConstructor { + new (config: VitestRunnerConfig): VitestRunner +} + +export interface VitestRunner { + /** + * First thing that's getting called before actually collecting and running tests. + */ + onBeforeCollect?(paths: string[]): unknown + /** + * Called after collecting tests and before "onBeforeRun". + */ + onCollected?(files: File[]): unknown + + /** + * Called before running a single test. Doesn't have "result" yet. + */ + onBeforeRunTest?(test: Test): unknown + /** + * Called before actually running the test function. Already has "result" with "state" and "startTime". + */ + onBeforeTryTest?(test: Test, retryCount: number): unknown + /** + * Called after result and state are set. + */ + onAfterRunTest?(test: Test): unknown + /** + * Called right after running the test function. Doesn't have new state yet. Will not be called, if the test function throws. + */ + onAfterTryTest?(test: Test, retryCount: number): unknown + + /** + * Called before running a single suite. Doesn't have "result" yet. + */ + onBeforeRunSuite?(suite: Suite): unknown + /** + * Called after running a single suite. Has state and result. + */ + onAfterRunSuite?(suite: Suite): unknown + + /** + * If defined, will be called instead of usual Vitest suite partition and handling. + * "before" and "after" hooks will not be ignored. + */ + runSuite?(suite: Suite): Promise + /** + * If defined, will be called instead of usual Vitest handling. Useful, if you have your custom test function. + * "before" and "after" hooks will not be ignored. + */ + runTest?(test: Test): Promise + + /** + * Called, when a task is updated. The same as "onTaskUpdate" in a reporter, but this is running in the same thread as tests. + */ + onTaskUpdate?(task: [string, TaskResult | undefined][]): Promise + + /** + * Called before running all tests in collected paths. + */ + onBeforeRun?(files: File[]): unknown + /** + * Called right after running all tests in collected paths. + */ + onAfterRun?(files: File[]): unknown + /** + * Called when new context for a test is defined. Useful, if you want to add custom properties to the context. + * If you only want to define custom context, consider using "beforeAll" in "setupFiles" instead. + */ + extendTestContext?(context: TestContext): TestContext + /** + * Called, when files are imported. Can be called in two situations: when collecting tests and when importing setup files. + */ + importFile(filepath: string, source: VitestRunnerImportSource): unknown + /** + * Publically available configuration. + */ + config: VitestRunnerConfig +} diff --git a/packages/runner/src/types/tasks.ts b/packages/runner/src/types/tasks.ts new file mode 100644 index 000000000000..c917deb5739e --- /dev/null +++ b/packages/runner/src/types/tasks.ts @@ -0,0 +1,232 @@ +import type { Awaitable } from '@vitest/utils' +import type { ChainableFunction } from '../utils/chain' +import type { ErrorWithDiff } from '../utils/error' + +export type RunMode = 'run' | 'skip' | 'only' | 'todo' +export type TaskState = RunMode | 'pass' | 'fail' + +export interface TaskBase { + id: string + name: string + mode: RunMode + concurrent?: boolean + shuffle?: boolean + suite?: Suite + file?: File + result?: TaskResult + retry?: number + meta?: any +} + +export interface TaskCustom extends TaskBase { + type: 'custom' +} + +export interface TaskResult { + state: TaskState + duration?: number + startTime?: number + heap?: number + /** + * @deprecated Use "errors" instead + */ + error?: ErrorWithDiff + errors?: ErrorWithDiff[] + htmlError?: string + hooks?: Partial> + retryCount?: number +} + +export type TaskResultPack = [id: string, result: TaskResult | undefined] + +export interface Suite extends TaskBase { + type: 'suite' + tasks: Task[] + filepath?: string + projectName?: string +} + +export interface File extends Suite { + filepath: string + collectDuration?: number + setupDuration?: number +} + +export interface Test extends TaskBase { + type: 'test' + suite: Suite + result?: TaskResult + fails?: boolean + context: TestContext & ExtraContext + onFailed?: OnTestFailedHandler[] +} + +export type Task = Test | Suite | TaskCustom | File + +export type DoneCallback = (error?: any) => void +export type TestFunction = (context: TestContext & ExtraContext) => Awaitable | void + +// jest's ExtractEachCallbackArgs +type ExtractEachCallbackArgs> = { + 1: [T[0]] + 2: [T[0], T[1]] + 3: [T[0], T[1], T[2]] + 4: [T[0], T[1], T[2], T[3]] + 5: [T[0], T[1], T[2], T[3], T[4]] + 6: [T[0], T[1], T[2], T[3], T[4], T[5]] + 7: [T[0], T[1], T[2], T[3], T[4], T[5], T[6]] + 8: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7]] + 9: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8]] + 10: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8], T[9]] + fallback: Array ? U : any> +}[T extends Readonly<[any]> + ? 1 + : T extends Readonly<[any, any]> + ? 2 + : T extends Readonly<[any, any, any]> + ? 3 + : T extends Readonly<[any, any, any, any]> + ? 4 + : T extends Readonly<[any, any, any, any, any]> + ? 5 + : T extends Readonly<[any, any, any, any, any, any]> + ? 6 + : T extends Readonly<[any, any, any, any, any, any, any]> + ? 7 + : T extends Readonly<[any, any, any, any, any, any, any, any]> + ? 8 + : T extends Readonly<[any, any, any, any, any, any, any, any, any]> + ? 9 + : T extends Readonly<[any, any, any, any, any, any, any, any, any, any]> + ? 10 + : 'fallback'] + +interface SuiteEachFunction { + (cases: ReadonlyArray): ( + name: string, + fn: (...args: T) => Awaitable, + ) => void + >(cases: ReadonlyArray): ( + name: string, + fn: (...args: ExtractEachCallbackArgs) => Awaitable, + ) => void + (cases: ReadonlyArray): ( + name: string, + fn: (...args: T[]) => Awaitable, + ) => void +} + +interface TestEachFunction { + (cases: ReadonlyArray): ( + name: string, + fn: (...args: T) => Awaitable, + options?: number | TestOptions, + ) => void + >(cases: ReadonlyArray): ( + name: string, + fn: (...args: ExtractEachCallbackArgs) => Awaitable, + options?: number | TestOptions, + ) => void + (cases: ReadonlyArray): ( + name: string, + fn: (...args: T[]) => Awaitable, + options?: number | TestOptions, + ) => void + (...args: [TemplateStringsArray, ...any]): ( + name: string, + fn: (...args: any[]) => Awaitable, + options?: number | TestOptions, + ) => void +} + +type ChainableTestAPI = ChainableFunction< + 'concurrent' | 'only' | 'skip' | 'todo' | 'fails', + [name: string, fn?: TestFunction, options?: number | TestOptions], + void, + { + each: TestEachFunction + (name: string, fn?: TestFunction, options?: number | TestOptions): void + } +> + +export interface TestOptions { + /** + * Test timeout. + */ + timeout?: number + /** + * Times to retry the test if fails. Useful for making flaky tests more stable. + * When retries is up, the last test error will be thrown. + * + * @default 1 + */ + retry?: number +} + +export type TestAPI = ChainableTestAPI & { + each: TestEachFunction + skipIf(condition: any): ChainableTestAPI + runIf(condition: any): ChainableTestAPI +} + +type ChainableSuiteAPI = ChainableFunction< + 'concurrent' | 'only' | 'skip' | 'todo' | 'shuffle', + [name: string, factory?: SuiteFactory, options?: number | TestOptions], + SuiteCollector, + { + each: TestEachFunction + (name: string, factory?: SuiteFactory): SuiteCollector + } +> + +export type SuiteAPI = ChainableSuiteAPI & { + each: SuiteEachFunction + skipIf(condition: any): ChainableSuiteAPI + runIf(condition: any): ChainableSuiteAPI +} + +export type HookListener = (...args: T) => Awaitable + +export type HookCleanupCallback = (() => Awaitable) | void + +export interface SuiteHooks { + beforeAll: HookListener<[Suite | File], HookCleanupCallback>[] + afterAll: HookListener<[Suite | File]>[] + beforeEach: HookListener<[TestContext & ExtraContext, Suite], HookCleanupCallback>[] + afterEach: HookListener<[TestContext & ExtraContext, Suite]>[] +} + +export interface SuiteCollector { + readonly name: string + readonly mode: RunMode + type: 'collector' + test: TestAPI + tasks: (Suite | TaskCustom | Test | SuiteCollector)[] + custom: (name: string) => TaskCustom + collect: (file?: File) => Promise + clear: () => void + on: >(name: T, ...fn: SuiteHooks[T]) => void +} + +export type SuiteFactory = (test: (name: string, fn: TestFunction) => void) => Awaitable + +export interface RuntimeContext { + tasks: (SuiteCollector | Test)[] + currentSuite: SuiteCollector | null +} + +export interface TestContext { + /** + * Metadata of the current test + */ + meta: Readonly + + /** + * Extract hooks on test failed + */ + onTestFailed: (fn: OnTestFailedHandler) => void +} + +export type OnTestFailedHandler = (result: TaskResult) => Awaitable + +export type SequenceHooks = 'stack' | 'list' | 'parallel' diff --git a/packages/vitest/src/runtime/chain.ts b/packages/runner/src/utils/chain.ts similarity index 100% rename from packages/vitest/src/runtime/chain.ts rename to packages/runner/src/utils/chain.ts diff --git a/packages/vitest/src/utils/collect.ts b/packages/runner/src/utils/collect.ts similarity index 100% rename from packages/vitest/src/utils/collect.ts rename to packages/runner/src/utils/collect.ts diff --git a/packages/vitest/src/runtime/error.ts b/packages/runner/src/utils/error.ts similarity index 87% rename from packages/vitest/src/runtime/error.ts rename to packages/runner/src/utils/error.ts index 15a56c331a44..34647659cfdc 100644 --- a/packages/vitest/src/runtime/error.ts +++ b/packages/runner/src/utils/error.ts @@ -1,7 +1,25 @@ -import util from 'util' -import { util as ChaiUtil } from 'chai' -import { stringify } from '@vitest/utils' -import { deepClone, getType, getWorkerState } from '../utils' +import { deepClone, format, getOwnProperties, getType, stringify } from '@vitest/utils' + +export interface ParsedStack { + method: string + file: string + line: number + column: number +} + +export interface ErrorWithDiff extends Error { + name: string + nameStr?: string + stack?: string + stackStr?: string + stacks?: ParsedStack[] + showDiff?: boolean + actual?: any + expected?: any + operator?: string + type?: string + frame?: string +} const IS_RECORD_SYMBOL = '@@__IMMUTABLE_RECORD__@@' const IS_COLLECTION_SYMBOL = '@@__IMMUTABLE_ITERABLE__@@' @@ -36,7 +54,7 @@ export function serializeError(val: any, seen = new WeakMap()): any { if (typeof Element !== 'undefined' && val instanceof Element) return val.tagName if (typeof val.asymmetricMatch === 'function') - return `${val.toString()} ${util.format(val.sample)}` + return `${val.toString()} ${format(val.sample)}` if (seen.has(val)) return seen.get(val) @@ -84,7 +102,11 @@ function normalizeErrorMessage(message: string) { return message.replace(/__vite_ssr_import_\d+__\./g, '') } -export function processError(err: any) { +interface ProcessErrorOptions { + outputDiffMaxSize?: number +} + +export function processError(err: any, options: ProcessErrorOptions = {}) { if (!err || typeof err !== 'object') return err // stack is not serialized in worker communication @@ -102,8 +124,7 @@ export function processError(err: any) { err.actual = replacedActual err.expected = replacedExpected - const workerState = getWorkerState() - const maxDiffSize = workerState.config.outputDiffMaxSize + const maxDiffSize = options.outputDiffMaxSize ?? 10000 if (typeof err.expected !== 'string') err.expected = stringify(err.expected, 10, { maxLength: maxDiffSize }) @@ -146,7 +167,7 @@ export function replaceAsymmetricMatcher(actual: any, expected: any, actualRepla return { replacedActual: actual, replacedExpected: expected } actualReplaced.add(actual) expectedReplaced.add(expected) - ChaiUtil.getOwnEnumerableProperties(expected).forEach((key) => { + getOwnProperties(expected).forEach((key) => { const expectedValue = expected[key] const actualValue = actual[key] if (isAsymmetricMatcher(expectedValue)) { diff --git a/packages/runner/src/utils/index.ts b/packages/runner/src/utils/index.ts new file mode 100644 index 000000000000..ebbe97f11de4 --- /dev/null +++ b/packages/runner/src/utils/index.ts @@ -0,0 +1,5 @@ +export * from './collect' +export * from './suite' +export * from './tasks' +export * from './chain' +export * from './error' diff --git a/packages/runner/src/utils/suite.ts b/packages/runner/src/utils/suite.ts new file mode 100644 index 000000000000..c60a71aaaa64 --- /dev/null +++ b/packages/runner/src/utils/suite.ts @@ -0,0 +1,22 @@ +import type { Suite, Task } from '../types' + +/** + * Partition in tasks groups by consecutive concurrent + */ +export function partitionSuiteChildren(suite: Suite) { + let tasksGroup: Task[] = [] + const tasksGroups: Task[][] = [] + for (const c of suite.tasks) { + if (tasksGroup.length === 0 || c.concurrent === tasksGroup[0].concurrent) { + tasksGroup.push(c) + } + else { + tasksGroups.push(tasksGroup) + tasksGroup = [c] + } + } + if (tasksGroup.length > 0) + tasksGroups.push(tasksGroup) + + return tasksGroups +} diff --git a/packages/runner/src/utils/tasks.ts b/packages/runner/src/utils/tasks.ts new file mode 100644 index 000000000000..e80fd52343d3 --- /dev/null +++ b/packages/runner/src/utils/tasks.ts @@ -0,0 +1,39 @@ +import { type Arrayable, toArray } from '@vitest/utils' +import type { Suite, Task, TaskCustom, Test } from '../types' + +function isAtomTest(s: Task): s is Test | TaskCustom { + return s.type === 'test' || s.type === 'custom' +} + +export function getTests(suite: Arrayable): (Test | TaskCustom)[] { + return toArray(suite).flatMap(s => isAtomTest(s) ? [s] : s.tasks.flatMap(c => isAtomTest(c) ? [c] : getTests(c))) +} + +export function getTasks(tasks: Arrayable = []): Task[] { + return toArray(tasks).flatMap(s => isAtomTest(s) ? [s] : [s, ...getTasks(s.tasks)]) +} + +export function getSuites(suite: Arrayable): Suite[] { + return toArray(suite).flatMap(s => s.type === 'suite' ? [s, ...getSuites(s.tasks)] : []) +} + +export function hasTests(suite: Arrayable): boolean { + return toArray(suite).some(s => s.tasks.some(c => isAtomTest(c) || hasTests(c))) +} + +export function hasFailed(suite: Arrayable): boolean { + return toArray(suite).some(s => s.result?.state === 'fail' || (s.type === 'suite' && hasFailed(s.tasks))) +} + +export function getNames(task: Task) { + const names = [task.name] + let current: Task | undefined = task + + while (current?.suite || current?.file) { + current = current.suite || current.file + if (current?.name) + names.unshift(current.name) + } + + return names +} diff --git a/packages/runner/types.d.ts b/packages/runner/types.d.ts new file mode 100644 index 000000000000..26a125423d41 --- /dev/null +++ b/packages/runner/types.d.ts @@ -0,0 +1 @@ +export * from './dist/types.js' diff --git a/packages/runner/utils.d.ts b/packages/runner/utils.d.ts new file mode 100644 index 000000000000..e3f344e48a8d --- /dev/null +++ b/packages/runner/utils.d.ts @@ -0,0 +1 @@ +export * from './dist/utils.js' diff --git a/packages/ui/client/components/views/ViewReport.cy.tsx b/packages/ui/client/components/views/ViewReport.cy.tsx index 25c967c5c2af..acefd83b7401 100644 --- a/packages/ui/client/components/views/ViewReport.cy.tsx +++ b/packages/ui/client/components/views/ViewReport.cy.tsx @@ -1,6 +1,9 @@ import { faker } from '@faker-js/faker' import ViewReport from './ViewReport.vue' import type { File } from '#types' +import { config } from '~/composables/client' + +config.value.root = '' const taskErrorSelector = '.task-error' const viewReportSelector = '[data-testid=view-report]' diff --git a/packages/ui/client/components/views/ViewReport.vue b/packages/ui/client/components/views/ViewReport.vue index ebd8a97cc470..51188bcba22e 100644 --- a/packages/ui/client/components/views/ViewReport.vue +++ b/packages/ui/client/components/views/ViewReport.vue @@ -18,7 +18,7 @@ function collectFailed(task: Task, level: number): LeveledTask[] { if (task.result?.state !== 'fail') return [] - if (task.type === 'test' || task.type === 'benchmark' || task.type === 'typecheck') + if (task.type === 'test' || task.type === 'custom') return [{ ...task, level }] else return [{ ...task, level }, ...task.tasks.flatMap(t => collectFailed(t, level + 1))] diff --git a/packages/ui/cypress.config.ts b/packages/ui/cypress.config.ts index 27c6557c5b10..c95ce2dd80e7 100644 --- a/packages/ui/cypress.config.ts +++ b/packages/ui/cypress.config.ts @@ -9,6 +9,15 @@ export default defineConfig({ framework: 'vue', bundler: 'vite', viteConfig: { + define: { + 'process.env.NODE_DEBUG': '"false"', + }, + resolve: { + alias: [ + { find: /^\@vitest\/utils/, replacement: resolve('../utils/src/') }, + { find: /^\@vitest\/runner/, replacement: resolve('../runner/src/') }, + ], + }, configFile: resolve('./cypress/vite.config.ts'), }, }, diff --git a/packages/ui/package.json b/packages/ui/package.json index a3516a3c4089..32f4ba896777 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -41,6 +41,8 @@ "dependencies": { "fast-glob": "^3.2.12", "flatted": "^3.2.7", + "pathe": "^1.1.0", + "picocolors": "^1.0.0", "sirv": "^2.0.2" }, "devDependencies": { @@ -53,6 +55,7 @@ "@unocss/reset": "^0.48.3", "@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue-jsx": "^3.0.0", + "@vitest/runner": "workspace:*", "@vitest/ws-client": "workspace:*", "@vueuse/core": "^9.10.0", "ansi-to-html": "^0.7.2", @@ -63,7 +66,6 @@ "d3-graph-controller": "^2.5.1", "diff": "^5.1.0", "floating-vue": "^2.0.0-y.0", - "picocolors": "^1.0.0", "rollup": "^2.79.1", "splitpanes": "^3.1.5", "unocss": "^0.48.3", diff --git a/packages/utils/diff.d.ts b/packages/utils/diff.d.ts new file mode 100644 index 000000000000..0a66b86595c0 --- /dev/null +++ b/packages/utils/diff.d.ts @@ -0,0 +1 @@ +export * from './dist/diff.js' diff --git a/packages/utils/helpers.d.ts b/packages/utils/helpers.d.ts new file mode 100644 index 000000000000..0add1d0f2bd1 --- /dev/null +++ b/packages/utils/helpers.d.ts @@ -0,0 +1 @@ +export * from './dist/helpers.js' diff --git a/packages/utils/package.json b/packages/utils/package.json index dbacf907d39f..3a88debeba29 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -39,6 +39,7 @@ "dependencies": { "cli-truncate": "^3.1.0", "diff": "^5.1.0", + "loupe": "^2.3.6", "picocolors": "^1.0.0", "pretty-format": "^27.5.1" }, diff --git a/packages/utils/src/colors.ts b/packages/utils/src/colors.ts new file mode 100644 index 000000000000..6c31d625d98f --- /dev/null +++ b/packages/utils/src/colors.ts @@ -0,0 +1,46 @@ +import type p from 'picocolors' +import type { Formatter } from 'picocolors/types' +import { SAFE_COLORS_SYMBOL } from './constants' + +const colors = [ + 'reset', + 'bold', + 'dim', + 'italic', + 'underline', + 'inverse', + 'hidden', + 'strikethrough', + 'black', + 'red', + 'green', + 'yellow', + 'blue', + 'magenta', + 'cyan', + 'white', + 'gray', + 'bgBlack', + 'bgRed', + 'bgGreen', + 'bgYellow', + 'bgBlue', + 'bgMagenta', + 'bgCyan', + 'bgWhite', +] as const + +const formatter: Formatter = str => String(str) + +const defaultColors = colors.reduce((acc, key) => { + acc[key] = formatter + return acc +}, { isColorSupported: false } as typeof p) + +export function getColors(): typeof p { + return (globalThis as any)[SAFE_COLORS_SYMBOL] || defaultColors +} + +export function setColors(colors: typeof p) { + (globalThis as any)[SAFE_COLORS_SYMBOL] = colors +} diff --git a/packages/utils/src/constants.ts b/packages/utils/src/constants.ts new file mode 100644 index 000000000000..1b285a267dab --- /dev/null +++ b/packages/utils/src/constants.ts @@ -0,0 +1,2 @@ +export const SAFE_TIMERS_SYMBOL = Symbol('vitest:SAFE_TIMERS') +export const SAFE_COLORS_SYMBOL = Symbol('vitest:SAFE_COLORS') diff --git a/packages/utils/src/diff.ts b/packages/utils/src/diff.ts index 1b720554c974..5a2cc5cc0a2c 100644 --- a/packages/utils/src/diff.ts +++ b/packages/utils/src/diff.ts @@ -1,4 +1,3 @@ -import c from 'picocolors' import * as diff from 'diff' import cliTruncate from 'cli-truncate' @@ -6,12 +5,17 @@ export function formatLine(line: string, outputTruncateLength?: number) { return cliTruncate(line, (outputTruncateLength ?? (process.stdout?.columns || 80)) - 4) } +type Color = (str: string) => string + export interface DiffOptions { - noColor?: boolean outputDiffMaxLines?: number outputTruncateLength?: number outputDiffLines?: number showLegend?: boolean + + colorSuccess?: Color + colorError?: Color + colorDim?: Color } /** @@ -27,7 +31,7 @@ export function unifiedDiff(actual: string, expected: string, options: DiffOptio if (actual === expected) return '' - const { outputTruncateLength, outputDiffLines, outputDiffMaxLines, noColor, showLegend = true } = options + const { outputTruncateLength, outputDiffLines, outputDiffMaxLines, showLegend = true } = options const indent = ' ' const diffLimit = outputDiffLines || 15 @@ -41,9 +45,9 @@ export function unifiedDiff(actual: string, expected: string, options: DiffOptio let previousCount = 0 const str = (str: string) => str - const dim = noColor ? str : c.dim - const green = noColor ? str : c.green - const red = noColor ? str : c.red + const dim = options.colorDim || str + const green = options.colorSuccess || str + const red = options.colorError || str function preprocess(line: string) { if (!line || line.match(/\\ No newline/)) return diff --git a/packages/utils/src/display.ts b/packages/utils/src/display.ts new file mode 100644 index 000000000000..59403888944a --- /dev/null +++ b/packages/utils/src/display.ts @@ -0,0 +1,46 @@ +import util from 'util' +// @ts-expect-error doesn't have types +import loupeImport from 'loupe' + +const loupe = (typeof loupeImport.default === 'function' ? loupeImport.default : loupeImport) + +export function format(...args: any[]) { + return util.format(...args) +} + +// chai utils +export function inspect(obj: unknown): string { + return loupe(obj, { + depth: 2, + truncate: 40, + }) +} + +export function objDisplay(obj: unknown) { + const truncateThreshold = 40 + const str = inspect(obj) + const type = Object.prototype.toString.call(obj) + + if (str.length >= truncateThreshold) { + if (type === '[object Function]') { + const fn = obj as () => void + return !fn.name || fn.name === '' + ? '[Function]' + : `[Function: ${fn.name}]` + } + else if (type === '[object Array]') { + return `[ Array(${(obj as []).length}) ]` + } + else if (type === '[object Object]') { + const keys = Object.keys(obj as {}) + const kstr = keys.length > 2 + ? `${keys.splice(0, 2).join(', ')}, ...` + : keys.join(', ') + return `{ Object (${kstr}) }` + } + else { + return str + } + } + return str +} diff --git a/packages/utils/src/helpers.ts b/packages/utils/src/helpers.ts index f2f963ebaae9..ed03ab42daaa 100644 --- a/packages/utils/src/helpers.ts +++ b/packages/utils/src/helpers.ts @@ -1,3 +1,5 @@ +import type { Arrayable, Nullable } from './types' + export function assertTypes(value: unknown, name: string, types: string[]): void { const receivedType = typeof value const pass = types.includes(receivedType) @@ -5,6 +7,105 @@ export function assertTypes(value: unknown, name: string, types: string[]): void throw new TypeError(`${name} value must be ${types.join(' or ')}, received "${receivedType}"`) } +export function slash(path: string) { + return path.replace(/\\/g, '/') +} + +export function toArray(array?: Nullable>): Array { + if (array === null || array === undefined) + array = [] + + if (Array.isArray(array)) + return array + + return [array] +} + export function isObject(item: unknown): boolean { return item != null && typeof item === 'object' && !Array.isArray(item) } + +function isFinalObj(obj: any) { + return obj === Object.prototype || obj === Function.prototype || obj === RegExp.prototype +} + +export function getType(value: unknown): string { + return Object.prototype.toString.apply(value).slice(8, -1) +} + +function collectOwnProperties(obj: any, collector: Set | ((key: string | symbol) => void)) { + const collect = typeof collector === 'function' ? collector : (key: string | symbol) => collector.add(key) + Object.getOwnPropertyNames(obj).forEach(collect) + Object.getOwnPropertySymbols(obj).forEach(collect) +} + +export function getOwnProperties(obj: any) { + const ownProps = new Set() + if (isFinalObj(obj)) + return [] + collectOwnProperties(obj, ownProps) + return Array.from(ownProps) +} + +export function deepClone(val: T): T { + const seen = new WeakMap() + return clone(val, seen) +} + +export function clone(val: T, seen: WeakMap): T { + let k: any, out: any + if (seen.has(val)) + return seen.get(val) + if (Array.isArray(val)) { + out = Array(k = val.length) + seen.set(val, out) + while (k--) + out[k] = clone(val[k], seen) + return out as any + } + + if (Object.prototype.toString.call(val) === '[object Object]') { + out = Object.create(Object.getPrototypeOf(val)) + seen.set(val, out) + // we don't need properties from prototype + const props = getOwnProperties(val) + for (const k of props) + out[k] = clone((val as any)[k], seen) + return out + } + + return val +} + +export function noop() {} + +export function objectAttr(source: any, path: string, defaultValue = undefined) { + // a[3].b -> a.3.b + const paths = path.replace(/\[(\d+)\]/g, '.$1').split('.') + let result = source + for (const p of paths) { + result = Object(result)[p] + if (result === undefined) + return defaultValue + } + return result +} + +type DeferPromise = Promise & { + resolve: (value: T | PromiseLike) => void + reject: (reason?: any) => void +} + +export function createDefer(): DeferPromise { + let resolve: ((value: T | PromiseLike) => void) | null = null + let reject: ((reason?: any) => void) | null = null + + const p = new Promise((_resolve, _reject) => { + resolve = _resolve + reject = _reject + }) as DeferPromise + + p.resolve = resolve! + p.reject = reject! + return p +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index db9ed73002ee..953be9159075 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,4 +1,8 @@ -export * from './diff' export * from './helpers' export * from './types' export * from './stringify' +export * from './timers' +export * from './random' +export * from './display' +export * from './constants' +export * from './colors' diff --git a/packages/utils/src/random.ts b/packages/utils/src/random.ts new file mode 100644 index 000000000000..7cd33217275c --- /dev/null +++ b/packages/utils/src/random.ts @@ -0,0 +1,21 @@ +const RealDate = Date + +function random(seed: number) { + const x = Math.sin(seed++) * 10000 + return x - Math.floor(x) +} + +export function shuffle(array: T[], seed = RealDate.now()): T[] { + let length = array.length + + while (length) { + const index = Math.floor(random(seed) * length--) + + const previous = array[length] + array[length] = array[index] + array[index] = previous + ++seed + } + + return array +} diff --git a/packages/utils/src/timers.ts b/packages/utils/src/timers.ts new file mode 100644 index 000000000000..15ded9f88261 --- /dev/null +++ b/packages/utils/src/timers.ts @@ -0,0 +1,35 @@ +import { SAFE_TIMERS_SYMBOL } from './constants' + +export function getSafeTimers() { + const { + setTimeout: safeSetTimeout, + setInterval: safeSetInterval, + clearInterval: safeClearInterval, + clearTimeout: safeClearTimeout, + } = (globalThis as any)[SAFE_TIMERS_SYMBOL] || globalThis + + return { + setTimeout: safeSetTimeout, + setInterval: safeSetInterval, + clearInterval: safeClearInterval, + clearTimeout: safeClearTimeout, + } +} + +export function setSafeTimers() { + const { + setTimeout: safeSetTimeout, + setInterval: safeSetInterval, + clearInterval: safeClearInterval, + clearTimeout: safeClearTimeout, + } = globalThis + + const timers = { + setTimeout: safeSetTimeout, + setInterval: safeSetInterval, + clearInterval: safeClearInterval, + clearTimeout: safeClearTimeout, + } + + ;(globalThis as any)[SAFE_TIMERS_SYMBOL] = timers +} diff --git a/packages/vite-node/package.json b/packages/vite-node/package.json index d390fc080a44..190144d7e006 100644 --- a/packages/vite-node/package.json +++ b/packages/vite-node/package.json @@ -79,7 +79,7 @@ "cac": "^6.7.14", "debug": "^4.3.4", "mlly": "^1.1.0", - "pathe": "^0.2.0", + "pathe": "^1.1.0", "picocolors": "^1.0.0", "source-map": "^0.6.1", "source-map-support": "^0.5.21", diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index e5d5f935bb97..1786957b97c3 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -10,6 +10,8 @@ import { VALID_ID_PREFIX, cleanUrl, isInternalRequest, isPrimitive, normalizeMod import type { HotContext, ModuleCache, ViteNodeRunnerOptions } from './types' import { extractSourceMap } from './source-map' +const { setTimeout, clearTimeout } = globalThis + const debugExecute = createDebug('vite-node:client:execute') const debugNative = createDebug('vite-node:client:native') diff --git a/packages/vitest/LICENSE.md b/packages/vitest/LICENSE.md index 60565f1af0d4..5d7e1b156bf8 100644 --- a/packages/vitest/LICENSE.md +++ b/packages/vitest/LICENSE.md @@ -250,7 +250,7 @@ Repository: chalk/ansi-regex > MIT License > -> Copyright (c) Sindre Sorhus (https://sindresorhus.com) +> Copyright (c) Sindre Sorhus (sindresorhus.com) > > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: > @@ -267,7 +267,7 @@ Repository: chalk/ansi-styles > MIT License > -> Copyright (c) Sindre Sorhus (https://sindresorhus.com) +> Copyright (c) Sindre Sorhus (sindresorhus.com) > > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: > @@ -427,44 +427,6 @@ Repository: git@github.com:moxystudio/node-cross-spawn.git --------------------------------------- -## diff -License: BSD-3-Clause -Repository: git://github.com/kpdecker/jsdiff.git - -> Software License Agreement (BSD License) -> -> Copyright (c) 2009-2015, Kevin Decker -> -> All rights reserved. -> -> Redistribution and use of this software in source and binary forms, with or without modification, -> are permitted provided that the following conditions are met: -> -> * Redistributions of source code must retain the above -> copyright notice, this list of conditions and the -> following disclaimer. -> -> * Redistributions in binary form must reproduce the above -> copyright notice, this list of conditions and the -> following disclaimer in the documentation and/or other -> materials provided with the distribution. -> -> * Neither the name of Kevin Decker nor the names of its -> contributors may be used to endorse or promote products -> derived from this software without specific prior -> written permission. -> -> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -> IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -> FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -> CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -> DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -> DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER -> IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -> OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------- - ## eastasianwidth License: MIT By: Masaki Komagata @@ -1466,7 +1428,7 @@ Repository: sindresorhus/mimic-fn > MIT License > -> Copyright (c) Sindre Sorhus (https://sindresorhus.com) +> Copyright (c) Sindre Sorhus (sindresorhus.com) > > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: > @@ -1603,7 +1565,7 @@ Repository: sindresorhus/path-key > MIT License > -> Copyright (c) Sindre Sorhus (https://sindresorhus.com) +> Copyright (c) Sindre Sorhus (sindresorhus.com) > > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: > @@ -1613,57 +1575,6 @@ Repository: sindresorhus/path-key --------------------------------------- -## pathe -License: MIT -Repository: unjs/pathe - -> MIT License -> -> Copyright (c) 2021 UnJS -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in all -> copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -> SOFTWARE. -> -> -------------------------------------------------------------------------------- -> -> Copyright Joyent, Inc. and other Node contributors. -> -> Permission is hereby granted, free of charge, to any person obtaining a -> copy of this software and associated documentation files (the -> "Software"), to deal in the Software without restriction, including -> without limitation the rights to use, copy, modify, merge, publish, -> distribute, sublicense, and/or sell copies of the Software, and to permit -> persons to whom the Software is furnished to do so, subject to the -> following conditions: -> -> The above copyright notice and this permission notice shall be included -> in all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -> OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -> NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -> DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -> OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -> USE OR OTHER DEALINGS IN THE SOFTWARE. - ---------------------------------------- - ## picomatch License: MIT By: Jon Schlinkert diff --git a/packages/vitest/browser.d.ts b/packages/vitest/browser.d.ts index 174e295d4d9d..81bff543e8b8 100644 --- a/packages/vitest/browser.d.ts +++ b/packages/vitest/browser.d.ts @@ -1 +1 @@ -export * from './dist/browser' +export * from './dist/browser.js' diff --git a/packages/vitest/package.json b/packages/vitest/package.json index b5588b7a4853..61d9f3dda60a 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -45,14 +45,22 @@ "types": "./dist/node.d.ts", "import": "./dist/node.js" }, - "./environments": { - "types": "./dist/environments.d.ts", - "import": "./dist/environments.js" - }, "./browser": { "types": "./dist/browser.d.ts", "import": "./dist/browser.js" }, + "./runners": { + "types": "./dist/runners.d.ts", + "import": "./dist/runners.js" + }, + "./suite": { + "types": "./dist/suite.d.ts", + "import": "./dist/suite.js" + }, + "./environments": { + "types": "./dist/environments.d.ts", + "import": "./dist/environments.js" + }, "./config": { "types": "./config.d.ts", "require": "./dist/config.cjs", @@ -109,12 +117,18 @@ "@types/chai": "^4.3.4", "@types/chai-subset": "^1.3.3", "@types/node": "*", + "@vitest/expect": "workspace:*", + "@vitest/runner": "workspace:*", + "@vitest/spy": "workspace:*", + "@vitest/ui": "workspace:*", + "@vitest/utils": "workspace:*", "acorn": "^8.8.1", "acorn-walk": "^8.2.0", "cac": "^6.7.14", "chai": "^4.3.7", "debug": "^4.3.4", "local-pkg": "^0.4.2", + "pathe": "^1.1.0", "picocolors": "^1.0.0", "source-map": "^0.6.1", "std-env": "^3.3.1", @@ -136,10 +150,6 @@ "@types/natural-compare": "^1.4.1", "@types/prompts": "^2.4.2", "@types/sinonjs__fake-timers": "^8.1.2", - "@vitest/expect": "workspace:*", - "@vitest/spy": "workspace:*", - "@vitest/ui": "workspace:*", - "@vitest/utils": "workspace:*", "birpc": "^0.2.3", "chai-subset": "^1.6.0", "cli-truncate": "^3.1.0", @@ -159,7 +169,6 @@ "mlly": "^1.1.0", "natural-compare": "^1.4.0", "p-limit": "^4.0.0", - "pathe": "^0.2.0", "pkg-types": "^1.0.1", "pretty-format": "^27.5.1", "prompts": "^2.4.2", diff --git a/packages/vitest/rollup.config.js b/packages/vitest/rollup.config.js index 79d833cdfaf4..d29c799a669b 100644 --- a/packages/vitest/rollup.config.js +++ b/packages/vitest/rollup.config.js @@ -15,23 +15,26 @@ import pkg from './package.json' const entries = [ 'src/index.ts', - 'src/browser.ts', 'src/node/cli.ts', 'src/node/cli-wrapper.ts', 'src/node.ts', + 'src/suite.ts', + 'src/browser.ts', + 'src/runners.ts', 'src/environments.ts', 'src/runtime/worker.ts', 'src/runtime/loader.ts', 'src/runtime/entry.ts', - 'src/runtime/suite.ts', 'src/integrations/spy.ts', ] const dtsEntries = [ 'src/index.ts', 'src/node.ts', - 'src/browser.ts', 'src/environments.ts', + 'src/browser.ts', + 'src/runners.ts', + 'src/suite.ts', 'src/config.ts', ] @@ -47,6 +50,9 @@ const external = [ 'vite-node/client', 'vite-node/server', 'vite-node/utils', + '@vitest/utils/diff', + '@vitest/runner/utils', + '@vitest/runner/types', ] const plugins = [ @@ -67,8 +73,11 @@ export default ({ watch }) => defineConfig([ dir: 'dist', format: 'esm', chunkFileNames: (chunkInfo) => { - const id = chunkInfo.facadeModuleId || Object.keys(chunkInfo.modules).find(i => !i.includes('node_modules') && i.includes('src/')) + let id = chunkInfo.facadeModuleId || Object.keys(chunkInfo.modules).find(i => !i.includes('node_modules') && (i.includes('src/') || i.includes('src\\'))) if (id) { + id = normalize(id) + if (id.includes('runtime/runners')) + return 'runners-chunk.js' const parts = Array.from( new Set(relative(process.cwd(), id).split(/\//g) .map(i => i.replace(/\..*$/, '')) diff --git a/packages/vitest/runners.d.ts b/packages/vitest/runners.d.ts new file mode 100644 index 000000000000..0477c34a9a47 --- /dev/null +++ b/packages/vitest/runners.d.ts @@ -0,0 +1 @@ +export * from './dist/runners.js' diff --git a/packages/vitest/src/api/setup.ts b/packages/vitest/src/api/setup.ts index aed3de53b9ed..280698ef3b18 100644 --- a/packages/vitest/src/api/setup.ts +++ b/packages/vitest/src/api/setup.ts @@ -1,4 +1,4 @@ -import { promises as fs } from 'node:fs' +import { existsSync, promises as fs } from 'node:fs' import type { BirpcReturn } from 'birpc' import { createBirpc } from 'birpc' @@ -51,12 +51,26 @@ export function setup(ctx: Vitest) { getFiles() { return ctx.state.getFiles() }, - async getPaths() { - return await ctx.state.getPaths() + getPaths() { + return ctx.state.getPaths() }, - readFile(id) { + resolveSnapshotPath(testPath) { + return ctx.snapshot.resolvePath(testPath) + }, + removeFile(id) { + return fs.unlink(id) + }, + createDirectory(id) { + return fs.mkdir(id, { recursive: true }) + }, + async readFile(id) { + if (!existsSync(id)) + return null return fs.readFile(id, 'utf-8') }, + snapshotSaved(snapshot) { + ctx.snapshot.add(snapshot) + }, writeFile(id, content) { return fs.writeFile(id, content, 'utf-8') }, diff --git a/packages/vitest/src/api/types.ts b/packages/vitest/src/api/types.ts index 4e7aa64b6d9c..4275d9d9ca66 100644 --- a/packages/vitest/src/api/types.ts +++ b/packages/vitest/src/api/types.ts @@ -1,5 +1,5 @@ import type { TransformResult } from 'vite' -import type { File, ModuleGraphData, Reporter, ResolvedConfig, TaskResultPack } from '../types' +import type { File, ModuleGraphData, Reporter, ResolvedConfig, SnapshotResult, TaskResultPack } from '../types' export interface TransformResultWithSource extends TransformResult { source?: string @@ -11,12 +11,16 @@ export interface WebSocketHandlers { onCollected(files?: File[]): Promise onTaskUpdate(packs: TaskResultPack[]): void getFiles(): File[] - getPaths(): Promise + getPaths(): string[] getConfig(): ResolvedConfig + resolveSnapshotPath(testPath: string): string getModuleGraph(id: string): Promise getTransformResult(id: string): Promise - readFile(id: string): Promise + readFile(id: string): Promise writeFile(id: string, content: string): Promise + removeFile(id: string): Promise + createDirectory(id: string): Promise + snapshotSaved(snapshot: SnapshotResult): void rerun(files: string[]): Promise updateSnapshot(file?: File): Promise } diff --git a/packages/vitest/src/browser.ts b/packages/vitest/src/browser.ts index 8b656e1e98b9..27eabf5e6121 100644 --- a/packages/vitest/src/browser.ts +++ b/packages/vitest/src/browser.ts @@ -1,6 +1,3 @@ -export { suite, test, describe, it } from './runtime/suite' -export * from './runtime/hooks' -export * from './integrations/chai' -export { startTests } from './runtime/run' -export { setupGlobalEnv } from './runtime/setup' -export * from './types' +export { startTests } from '@vitest/runner' +export { setupCommonEnv } from './runtime/setup.common' +export { setupSnapshotEnvironment } from './integrations/snapshot/env' diff --git a/packages/vitest/src/index.ts b/packages/vitest/src/index.ts index 8b8ddedbbc09..b31ce92a990a 100644 --- a/packages/vitest/src/index.ts +++ b/packages/vitest/src/index.ts @@ -1,11 +1,21 @@ -export { suite, test, describe, it, bench } from './runtime/suite' -export * from './runtime/hooks' -export * from './runtime/utils' +export { + suite, + test, + describe, + it, + beforeAll, + beforeEach, + afterAll, + afterEach, + onTestFailed, +} from '@vitest/runner' +export { bench } from './runtime/benchmark' export { runOnce, isFirstRun } from './integrations/run-once' export * from './integrations/chai' export * from './integrations/vi' export * from './integrations/utils' +export type { SnapshotEnvironment } from './integrations/snapshot/env' export * from './types' export * from './api/types' diff --git a/packages/vitest/src/integrations/chai/index.ts b/packages/vitest/src/integrations/chai/index.ts index b245b926ad15..81bd78570ad8 100644 --- a/packages/vitest/src/integrations/chai/index.ts +++ b/packages/vitest/src/integrations/chai/index.ts @@ -1,8 +1,8 @@ import * as chai from 'chai' import './setup' +import type { Test } from '@vitest/runner' import { GLOBAL_EXPECT, getState, setState } from '@vitest/expect' import type { MatcherState } from '../../types/chai' -import type { Test } from '../../types' import { getCurrentEnvironment, getFullName } from '../../utils' export function createExpect(test?: Test) { diff --git a/packages/vitest/src/integrations/snapshot/client.ts b/packages/vitest/src/integrations/snapshot/client.ts index a28dabefcfb9..ee5d8a46bed8 100644 --- a/packages/vitest/src/integrations/snapshot/client.ts +++ b/packages/vitest/src/integrations/snapshot/client.ts @@ -1,8 +1,9 @@ import { expect } from 'chai' import { equals, iterableEquality, subsetEquality } from '@vitest/expect' -import type { Test } from '../../types' +import type { Test } from '@vitest/runner' +import { getNames } from '@vitest/runner/utils' import { rpc } from '../../runtime/rpc' -import { getNames, getWorkerState } from '../../utils' +import { getWorkerState } from '../../utils' import { deepMergeSnapshot } from './port/utils' import SnapshotState from './port/state' @@ -38,9 +39,8 @@ export class SnapshotClient { if (!this.getSnapshotState(test)) { this.snapshotStateMap.set( filePath, - new SnapshotState( + await SnapshotState.create( filePath, - await rpc().resolveSnapshotPath(filePath), getWorkerState().config.snapshotOptions, ), ) diff --git a/packages/vitest/src/integrations/snapshot/env.ts b/packages/vitest/src/integrations/snapshot/env.ts new file mode 100644 index 000000000000..518a3dfb2cf8 --- /dev/null +++ b/packages/vitest/src/integrations/snapshot/env.ts @@ -0,0 +1,19 @@ +export interface SnapshotEnvironment { + resolvePath(filepath: string): Promise + prepareDirectory(filepath: string): Promise + saveSnapshotFile(filepath: string, snapshot: string): Promise + readSnapshotFile(filepath: string): Promise + removeSnapshotFile(filepath: string): Promise +} + +let _snapshotEnvironment: SnapshotEnvironment + +export function setupSnapshotEnvironment(environment: SnapshotEnvironment) { + _snapshotEnvironment = environment +} + +export function getSnapshotEnironment() { + if (!_snapshotEnvironment) + throw new Error('Snapshot environment is not setup') + return _snapshotEnvironment +} diff --git a/packages/vitest/src/integrations/snapshot/environments/node.ts b/packages/vitest/src/integrations/snapshot/environments/node.ts new file mode 100644 index 000000000000..e0b7326cb721 --- /dev/null +++ b/packages/vitest/src/integrations/snapshot/environments/node.ts @@ -0,0 +1,28 @@ +import { existsSync, promises as fs } from 'node:fs' +import { rpc } from '../../../runtime/rpc' +import type { SnapshotEnvironment } from '../env' + +export class NodeSnapshotEnvironment implements SnapshotEnvironment { + resolvePath(filepath: string): Promise { + return rpc().resolveSnapshotPath(filepath) + } + + async prepareDirectory(filepath: string): Promise { + await fs.mkdir(filepath, { recursive: true }) + } + + async saveSnapshotFile(filepath: string, snapshot: string): Promise { + await fs.writeFile(filepath, snapshot, 'utf-8') + } + + async readSnapshotFile(filepath: string): Promise { + if (!existsSync(filepath)) + return null + return fs.readFile(filepath, 'utf-8') + } + + async removeSnapshotFile(filepath: string): Promise { + if (existsSync(filepath)) + await fs.unlink(filepath) + } +} diff --git a/packages/vitest/src/integrations/snapshot/port/inlineSnapshot.ts b/packages/vitest/src/integrations/snapshot/port/inlineSnapshot.ts index d62a00bf409b..c19fde2a590b 100644 --- a/packages/vitest/src/integrations/snapshot/port/inlineSnapshot.ts +++ b/packages/vitest/src/integrations/snapshot/port/inlineSnapshot.ts @@ -1,7 +1,7 @@ -import { promises as fs } from 'fs' import type MagicString from 'magic-string' import { lineSplitRE, offsetToLineNumber, positionToOffset } from '../../../utils/source-map' import { getCallLastIndex } from '../../../utils' +import { getSnapshotEnironment } from '../env' export interface InlineSnapshot { snapshot: string @@ -13,11 +13,12 @@ export interface InlineSnapshot { export async function saveInlineSnapshots( snapshots: Array, ) { + const environment = getSnapshotEnironment() const MagicString = (await import('magic-string')).default const files = new Set(snapshots.map(i => i.file)) await Promise.all(Array.from(files).map(async (file) => { const snaps = snapshots.filter(i => i.file === file) - const code = await fs.readFile(file, 'utf8') + const code = await environment.readSnapshotFile(file) as string const s = new MagicString(code) for (const snap of snaps) { @@ -27,7 +28,7 @@ export async function saveInlineSnapshots( const transformed = s.toString() if (transformed !== code) - await fs.writeFile(file, transformed, 'utf-8') + await environment.saveSnapshotFile(file, transformed) })) } diff --git a/packages/vitest/src/integrations/snapshot/port/state.ts b/packages/vitest/src/integrations/snapshot/port/state.ts index a74563880f87..de7ad8c84143 100644 --- a/packages/vitest/src/integrations/snapshot/port/state.ts +++ b/packages/vitest/src/integrations/snapshot/port/state.ts @@ -5,11 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import fs from 'node:fs' import type { OptionsReceived as PrettyFormatOptions } from 'pretty-format' import type { ParsedStack, SnapshotData, SnapshotMatchOptions, SnapshotResult, SnapshotStateOptions, SnapshotUpdateState } from '../../../types' import { slash } from '../../../utils' import { parseStacktrace } from '../../../utils/source-map' +import type { SnapshotEnvironment } from '../env' +import { getSnapshotEnironment } from '../env' import type { InlineSnapshot } from './inlineSnapshot' import { saveInlineSnapshots } from './inlineSnapshot' @@ -46,6 +47,8 @@ export default class SnapshotState { private _inlineSnapshots: Array private _uncheckedKeys: Set private _snapshotFormat: PrettyFormatOptions + private _environment: SnapshotEnvironment + private _fileExists: boolean added: number expand: boolean @@ -53,15 +56,17 @@ export default class SnapshotState { unmatched: number updated: number - constructor( + private constructor( public testFilePath: string, public snapshotPath: string, + snapshotContent: string | null, options: SnapshotStateOptions, ) { const { data, dirty } = getSnapshotData( - this.snapshotPath, - options.updateSnapshot, + snapshotContent, + options, ) + this._fileExists = snapshotContent != null // TODO: update on watch? this._initialData = data this._snapshotData = data this._dirty = dirty @@ -78,6 +83,17 @@ export default class SnapshotState { printBasicPrototype: false, ...options.snapshotFormat, } + this._environment = getSnapshotEnironment() + } + + static async create( + testFilePath: string, + options: SnapshotStateOptions, + ) { + const environment = getSnapshotEnironment() + const snapshotPath = await environment.resolvePath(testFilePath) + const content = await environment.readSnapshotFile(snapshotPath) + return new SnapshotState(testFilePath, snapshotPath, content, options) } markSnapshotsAsCheckedForTest(testName: string): void { @@ -151,16 +167,20 @@ export default class SnapshotState { } if ((this._dirty || this._uncheckedKeys.size) && !isEmpty) { - if (hasExternalSnapshots) + if (hasExternalSnapshots) { await saveSnapshotFile(this._snapshotData, this.snapshotPath) + this._fileExists = true + } if (hasInlineSnapshots) await saveInlineSnapshots(this._inlineSnapshots) status.saved = true } - else if (!hasExternalSnapshots && fs.existsSync(this.snapshotPath)) { - if (this._updateSnapshot === 'all') - fs.unlinkSync(this.snapshotPath) + else if (!hasExternalSnapshots && this._fileExists) { + if (this._updateSnapshot === 'all') { + await this._environment.removeSnapshotFile(this.snapshotPath) + this._fileExists = false + } status.deleted = true } @@ -209,7 +229,7 @@ export default class SnapshotState { const expectedTrimmed = prepareExpected(expected) const pass = expectedTrimmed === prepareExpected(receivedSerialized) const hasSnapshot = expected !== undefined - const snapshotIsPersisted = isInline || fs.existsSync(this.snapshotPath) + const snapshotIsPersisted = isInline || this._fileExists if (pass && !isInline) { // Executing a snapshot file as JavaScript and writing the strings back diff --git a/packages/vitest/src/integrations/snapshot/port/utils.ts b/packages/vitest/src/integrations/snapshot/port/utils.ts index 598f1ea25031..d4c828727202 100644 --- a/packages/vitest/src/integrations/snapshot/port/utils.ts +++ b/packages/vitest/src/integrations/snapshot/port/utils.ts @@ -5,15 +5,15 @@ * LICENSE file in the root directory of this source tree. */ -import fs from 'node:fs' import { dirname, join } from 'pathe' import naturalCompare from 'natural-compare' import type { OptionsReceived as PrettyFormatOptions } from 'pretty-format' import { format as prettyFormat, } from 'pretty-format' -import type { SnapshotData, SnapshotUpdateState } from '../../../types' +import type { SnapshotData, SnapshotStateOptions } from '../../../types' import { isObject } from '../../../utils' +import { getSnapshotEnironment } from '../env' import { getSerializers } from './plugins' // TODO: rewrite and clean up @@ -33,19 +33,20 @@ export const keyToTestName = (key: string): string => { } export const getSnapshotData = ( - snapshotPath: string, - update: SnapshotUpdateState, + content: string | null, + options: SnapshotStateOptions, ): { data: SnapshotData dirty: boolean } => { + const update = options.updateSnapshot const data = Object.create(null) let snapshotContents = '' let dirty = false - if (fs.existsSync(snapshotPath)) { + if (content != null) { try { - snapshotContents = fs.readFileSync(snapshotPath, 'utf8') + snapshotContents = content // eslint-disable-next-line no-new-func const populate = new Function('exports', snapshotContents) populate(data) @@ -130,9 +131,10 @@ function printBacktickString(str: string): string { return `\`${escapeBacktickString(str)}\`` } -export function ensureDirectoryExists(filePath: string): void { +export async function ensureDirectoryExists(filePath: string) { try { - fs.mkdirSync(join(dirname(filePath)), { recursive: true }) + const environment = getSnapshotEnironment() + await environment.prepareDirectory(join(dirname(filePath))) } catch { } } @@ -145,6 +147,7 @@ export async function saveSnapshotFile( snapshotData: SnapshotData, snapshotPath: string, ) { + const environment = getSnapshotEnironment() const snapshots = Object.keys(snapshotData) .sort(naturalCompare) .map( @@ -152,16 +155,16 @@ export async function saveSnapshotFile( ) const content = `${writeSnapshotVersion()}\n\n${snapshots.join('\n\n')}\n` - const skipWriting = fs.existsSync(snapshotPath) && (await fs?.promises.readFile(snapshotPath, 'utf8')) === content + const oldContent = await environment.readSnapshotFile(snapshotPath) + const skipWriting = oldContent && oldContent === content if (skipWriting) return - ensureDirectoryExists(snapshotPath) - await fs?.promises.writeFile( + await ensureDirectoryExists(snapshotPath) + await environment.saveSnapshotFile( snapshotPath, content, - 'utf-8', ) } diff --git a/packages/vitest/src/integrations/vi.ts b/packages/vitest/src/integrations/vi.ts index 1761494c6488..d67a481b8c0e 100644 --- a/packages/vitest/src/integrations/vi.ts +++ b/packages/vitest/src/integrations/vi.ts @@ -340,7 +340,7 @@ class VitestUtils { public resetConfig() { if (this._config) { const state = getWorkerState() - state.config = { ...this._config } + Object.assign(state.config, this._config) } } } diff --git a/packages/vitest/src/node/cli-api.ts b/packages/vitest/src/node/cli-api.ts index 8f104a10697e..62c245d3b8bc 100644 --- a/packages/vitest/src/node/cli-api.ts +++ b/packages/vitest/src/node/cli-api.ts @@ -4,7 +4,7 @@ import { EXIT_CODE_RESTART } from '../constants' import { CoverageProviderMap } from '../integrations/coverage' import { getEnvPackageName } from '../integrations/env' import type { UserConfig, Vitest, VitestRunMode } from '../types' -import { ensurePackageInstalled } from '../utils' +import { ensurePackageInstalled } from './pkg' import { createVitest } from './create' import { registerConsoleShortcuts } from './stdin' diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index 7ff6540be997..5f02006a161d 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -18,11 +18,11 @@ const extraInlineDeps = [ // Vite client /vite\w*\/dist\/client\/env.mjs/, // Vitest - /\/vitest\/dist\//, + /\/vitest\/dist\/(runners-chunk|entry)\.js/, // yarn's .store folder - /vitest-virtual-\w+\/dist/, + /vitest-virtual-\w+\/dist\/(runners-chunk|entry)\.js/, // cnpm - /@vitest\/dist/, + /@vitest\/dist\/(runners-chunk|entry)\.js/, // Nuxt '@nuxt/test-utils', ] @@ -129,6 +129,11 @@ export function resolveConfig( } } + if (resolved.runner) { + resolved.runner = resolveModule(resolved.runner, { paths: [resolved.root] }) + ?? resolve(resolved.root, resolved.runner) + } + // disable loader for Yarn PnP until Node implements chain loader // https://github.com/nodejs/node/pull/43772 resolved.deps.registerNodeLoader ??= false diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index 47033d2ef925..ab5c95156741 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -9,7 +9,7 @@ import { ViteNodeRunner } from 'vite-node/client' import { ViteNodeServer } from 'vite-node/server' import type { ArgumentsType, CoverageProvider, OnServerRestartHandler, Reporter, ResolvedConfig, UserConfig, VitestRunMode } from '../types' import { SnapshotManager } from '../integrations/snapshot/manager' -import { clearTimeout, deepMerge, hasFailed, noop, setTimeout, slash, toArray } from '../utils' +import { deepMerge, hasFailed, noop, slash, toArray } from '../utils' import { getCoverageProvider } from '../integrations/coverage' import { Typechecker } from '../typecheck/typechecker' import { createPool } from './pool' @@ -315,9 +315,15 @@ export class Vitest { async runFiles(paths: string[]) { paths = Array.from(new Set(paths)) + this.state.collectPaths(paths) + + await this.report('onPathsCollected', paths) + + if (this.config.browser) + return + // previous run await this.runningPromise - this.state.startCollectingPaths() // schedule the new run this.runningPromise = (async () => { @@ -344,7 +350,6 @@ export class Vitest { await this.cache.results.writeToCache() })() .finally(async () => { - this.state.finishCollectingPaths() if (!this.config.browser) await this.report('onFinished', this.state.getFiles(paths), this.state.getUnhandledErrors()) this.runningPromise = undefined diff --git a/packages/vitest/src/node/error.ts b/packages/vitest/src/node/error.ts index d7b27e53d094..362b9864ad43 100644 --- a/packages/vitest/src/node/error.ts +++ b/packages/vitest/src/node/error.ts @@ -3,12 +3,12 @@ import { existsSync, readFileSync } from 'fs' import { normalize, relative } from 'pathe' import c from 'picocolors' import cliTruncate from 'cli-truncate' +import { type DiffOptions, unifiedDiff } from '@vitest/utils/diff' import { stringify } from '@vitest/utils' import type { ErrorWithDiff, ParsedStack } from '../types' import { lineSplitRE, parseStacktrace, positionToOffset } from '../utils/source-map' import { F_POINTER } from '../utils/figures' import { TypeCheckError } from '../typecheck/typechecker' -import { type DiffOptions, unifiedDiff } from '../utils/diff' import type { Vitest } from './core' import { divider } from './reporters/renderers/utils' import type { Logger } from './logger' @@ -90,6 +90,9 @@ export async function printError(error: unknown, ctx: Vitest, options: PrintErro outputTruncateLength: ctx.config.outputTruncateLength, outputDiffLines: ctx.config.outputDiffLines, outputDiffMaxLines: ctx.config.outputDiffMaxLines, + colorDim: c.dim, + colorError: c.red, + colorSuccess: c.green, }) } } @@ -165,8 +168,8 @@ function handleImportOutsideModuleError(stack: string, ctx: Vitest) { export function displayDiff(actual: string, expected: string, console: Console, options: Omit = {}) { const diff = unifiedDiff(actual, expected, options) - const dim = options.noColor ? (s: string) => s : c.dim - const black = options.noColor ? (s: string) => s : c.black + const dim = options.colorDim || ((str: string) => str) + const black = options.colorDim ? c.black : (str: string) => str if (diff) console.error(diff + '\n') else if (actual && expected && actual !== '"undefined"' && expected !== '"undefined"') diff --git a/packages/vitest/src/node/pkg.ts b/packages/vitest/src/node/pkg.ts new file mode 100644 index 000000000000..f0775e93237c --- /dev/null +++ b/packages/vitest/src/node/pkg.ts @@ -0,0 +1,36 @@ +import c from 'picocolors' +import { isPackageExists } from 'local-pkg' +import { EXIT_CODE_RESTART } from '../constants' +import { isCI } from '../utils/env' + +export async function ensurePackageInstalled( + dependency: string, + root: string, +) { + if (isPackageExists(dependency, { paths: [root] })) + return true + + const promptInstall = !isCI && process.stdout.isTTY + + process.stderr.write(c.red(`${c.inverse(c.red(' MISSING DEP '))} Can not find dependency '${dependency}'\n\n`)) + + if (!promptInstall) + return false + + const prompts = await import('prompts') + const { install } = await prompts.prompt({ + type: 'confirm', + name: 'install', + message: c.reset(`Do you want to install ${c.green(dependency)}?`), + }) + + if (install) { + await (await import('@antfu/install-pkg')).installPackage(dependency, { dev: true }) + // TODO: somehow it fails to load the package after installation, remove this when it's fixed + process.stderr.write(c.yellow(`\nPackage ${dependency} installed, re-run the command to start.\n`)) + process.exit(EXIT_CODE_RESTART) + return true + } + + return false +} diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index 1e6bd5f97690..ff525f526fc5 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -2,7 +2,8 @@ import type { UserConfig as ViteConfig, Plugin as VitePlugin } from 'vite' import { relative } from 'pathe' import { configDefaults } from '../../defaults' import type { ResolvedConfig, UserConfig } from '../../types' -import { deepMerge, ensurePackageInstalled, notNullish, removeUndefinedValues } from '../../utils' +import { deepMerge, notNullish, removeUndefinedValues } from '../../utils' +import { ensurePackageInstalled } from '../pkg' import { resolveApiConfig } from '../config' import { Vitest } from '../core' import { generateScopedClassName } from '../../integrations/css/css-modules' diff --git a/packages/vitest/src/node/reporters/base.ts b/packages/vitest/src/node/reporters/base.ts index 80ac85e139d4..b78ae626825d 100644 --- a/packages/vitest/src/node/reporters/base.ts +++ b/packages/vitest/src/node/reporters/base.ts @@ -1,7 +1,7 @@ import { performance } from 'perf_hooks' import c from 'picocolors' import type { ErrorWithDiff, File, Reporter, Task, TaskResultPack, UserConsoleLog } from '../../types' -import { clearInterval, getFullName, getSuites, getTests, hasFailed, hasFailedSnapshot, isCI, isNode, relativePath, setInterval } from '../../utils' +import { getFullName, getSafeTimers, getSuites, getTests, hasFailed, hasFailedSnapshot, isCI, isNode, relativePath } from '../../utils' import type { Vitest } from '../../node' import { F_RIGHT } from '../../utils/figures' import { countTestErrors, divider, formatProjectName, formatTimeString, getStateString, getStateSymbol, pointer, renderSnapshotSummary } from './renderers/utils' @@ -84,7 +84,7 @@ export abstract class BaseReporter implements Reporter { // print short errors, full errors will be at the end in summary for (const test of failed) { - logger.log(c.red(` ${pointer} ${getFullName(test)}`)) + logger.log(c.red(` ${pointer} ${getFullName(test, c.dim(' > '))}`)) test.result?.errors?.forEach((e) => { logger.log(c.red(` ${F_RIGHT} ${(e as any)?.message}`)) }) @@ -103,7 +103,7 @@ export abstract class BaseReporter implements Reporter { else this.ctx.logger.log(WAIT_FOR_CHANGE_PASS) - const hints = [] + const hints: string[] = [] // TODO typecheck doesn't support these for now if (this.mode !== 'typecheck') hints.push(HELP_HINT) @@ -123,6 +123,7 @@ export abstract class BaseReporter implements Reporter { ] this.ctx.logger.logUpdate(BADGE_PADDING + LAST_RUN_TEXTS[0]) this._lastRunTimeout = 0 + const { setInterval } = getSafeTimers() this._lastRunTimer = setInterval( () => { this._lastRunTimeout += 1 @@ -137,6 +138,7 @@ export abstract class BaseReporter implements Reporter { } private resetLastRunLog() { + const { clearInterval } = getSafeTimers() clearInterval(this._lastRunTimer) this._lastRunTimer = undefined this.ctx.logger.logUpdate.clear() @@ -172,7 +174,7 @@ export abstract class BaseReporter implements Reporter { if (!this.shouldLog(log)) return const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : undefined - this.ctx.logger.log(c.gray(log.type + c.dim(` | ${task ? getFullName(task) : 'unknown test'}`))) + this.ctx.logger.log(c.gray(log.type + c.dim(` | ${task ? getFullName(task, c.dim(' > ')) : 'unknown test'}`))) process[log.type].write(`${log.content}\n`) } @@ -293,7 +295,7 @@ export abstract class BaseReporter implements Reporter { const group = bench.suite if (!group) continue - const groupName = getFullName(group) + const groupName = getFullName(group, c.dim(' > ')) logger.log(` ${bench.name}${c.dim(` - ${groupName}`)}`) const siblings = group.tasks .filter(i => i.result?.benchmark && i !== bench) @@ -322,7 +324,7 @@ export abstract class BaseReporter implements Reporter { for (const task of tasks) { const filepath = (task as File)?.filepath || '' const projectName = (task as File)?.projectName || task.file?.projectName - let name = getFullName(task) + let name = getFullName(task, c.dim(' > ')) if (filepath) name = `${name} ${c.dim(`[ ${this.relative(filepath)} ]`)}` diff --git a/packages/vitest/src/node/reporters/benchmark/table/index.ts b/packages/vitest/src/node/reporters/benchmark/table/index.ts index 86d363130054..fb599bc02234 100644 --- a/packages/vitest/src/node/reporters/benchmark/table/index.ts +++ b/packages/vitest/src/node/reporters/benchmark/table/index.ts @@ -1,5 +1,5 @@ import c from 'picocolors' -import type { UserConsoleLog } from '../../../../types' +import type { UserConsoleLog } from '../../../../types/general' import { BaseReporter } from '../../base' import type { ListRendererOptions } from '../../renderers/listRenderer' import { createTableRenderer } from './tableRender' diff --git a/packages/vitest/src/node/reporters/benchmark/table/tableRender.ts b/packages/vitest/src/node/reporters/benchmark/table/tableRender.ts index aaed86f520c2..ae251fe3d874 100644 --- a/packages/vitest/src/node/reporters/benchmark/table/tableRender.ts +++ b/packages/vitest/src/node/reporters/benchmark/table/tableRender.ts @@ -2,7 +2,7 @@ import c from 'picocolors' import cliTruncate from 'cli-truncate' import stripAnsi from 'strip-ansi' import type { Benchmark, BenchmarkResult, Task } from '../../../../types' -import { clearInterval, getTests, notNullish, setInterval } from '../../../../utils' +import { getTests, notNullish } from '../../../../utils' import { F_RIGHT } from '../../../../utils/figures' import type { Logger } from '../../../logger' import { getCols, getStateSymbol } from '../../renderers/utils' @@ -38,7 +38,7 @@ const tableHead = ['name', 'hz', 'min', 'max', 'mean', 'p75', 'p99', 'p995', 'p9 function renderTableHead(tasks: Task[]) { const benchs = tasks - .map(i => i.type === 'benchmark' ? i.result?.benchmark : undefined) + .map(i => i.meta?.benchmark ? i.result?.benchmark : undefined) .filter(notNullish) const allItems = benchs.map(renderBenchmarkItems).concat([tableHead]) return `${' '.repeat(3)}${tableHead.map((i, idx) => { @@ -70,7 +70,7 @@ function renderBenchmark(task: Benchmark, tasks: Task[]): string { return task.name const benchs = tasks - .map(i => i.type === 'benchmark' ? i.result?.benchmark : undefined) + .map(i => i.meta?.benchmark ? i.result?.benchmark : undefined) .filter(notNullish) const allItems = benchs.map(renderBenchmarkItems).concat([tableHead]) const items = renderBenchmarkItems(result) @@ -108,7 +108,7 @@ export function renderTree(tasks: Task[], options: ListRendererOptions, level = for (const task of tasks) { const padding = ' '.repeat(level ? 1 : 0) let prefix = '' - if (idx === 0 && task.type === 'benchmark') + if (idx === 0 && task.meta?.benchmark) prefix += `${renderTableHead(tasks)}\n${padding}` prefix += ` ${getStateSymbol(task)} ` @@ -132,8 +132,8 @@ export function renderTree(tasks: Task[], options: ListRendererOptions, level = if (level === 0) name = formatFilepath(name) - const body = task.type === 'benchmark' - ? renderBenchmark(task, tasks) + const body = task.meta?.benchmark + ? renderBenchmark(task as Benchmark, tasks) : name output.push(padding + prefix + body + suffix) diff --git a/packages/vitest/src/node/reporters/default.ts b/packages/vitest/src/node/reporters/default.ts index a509084bfd76..e94cb8f86883 100644 --- a/packages/vitest/src/node/reporters/default.ts +++ b/packages/vitest/src/node/reporters/default.ts @@ -1,5 +1,5 @@ import c from 'picocolors' -import type { UserConsoleLog } from '../../types' +import type { UserConsoleLog } from '../../types/general' import { BaseReporter } from './base' import type { ListRendererOptions } from './renderers/listRenderer' import { createListRenderer } from './renderers/listRenderer' diff --git a/packages/vitest/src/node/reporters/dot.ts b/packages/vitest/src/node/reporters/dot.ts index 792e85ce13e7..753f5d6778e7 100644 --- a/packages/vitest/src/node/reporters/dot.ts +++ b/packages/vitest/src/node/reporters/dot.ts @@ -1,5 +1,4 @@ -import type { UserConsoleLog } from '../../types' -import { setTimeout } from '../../utils' +import type { UserConsoleLog } from '../../types/general' import { BaseReporter } from './base' import { createDotRenderer } from './renderers/dotRenderer' import type { createListRenderer } from './renderers/listRenderer' diff --git a/packages/vitest/src/node/reporters/junit.ts b/packages/vitest/src/node/reporters/junit.ts index 7fccb602281b..4a61d1adf731 100644 --- a/packages/vitest/src/node/reporters/junit.ts +++ b/packages/vitest/src/node/reporters/junit.ts @@ -2,8 +2,10 @@ import { existsSync, promises as fs } from 'node:fs' import { hostname } from 'node:os' import { dirname, relative, resolve } from 'pathe' +import type { Task } from '@vitest/runner' +import type { ErrorWithDiff } from '@vitest/runner/utils' import type { Vitest } from '../../node' -import type { ErrorWithDiff, Reporter, Task } from '../../types' +import type { Reporter } from '../../types/reporter' import { parseStacktrace } from '../../utils/source-map' import { F_POINTER } from '../../utils/figures' import { getOutputFile } from '../../utils/config-helpers' @@ -99,7 +101,7 @@ export class JUnitReporter implements Reporter { } async writeElement(name: string, attrs: Record, children: () => Promise) { - const pairs = [] + const pairs: string[] = [] for (const key in attrs) { const attr = attrs[key] if (attr === undefined) diff --git a/packages/vitest/src/node/reporters/renderers/dotRenderer.ts b/packages/vitest/src/node/reporters/renderers/dotRenderer.ts index b478d39c027e..fd0964ce2e1a 100644 --- a/packages/vitest/src/node/reporters/renderers/dotRenderer.ts +++ b/packages/vitest/src/node/reporters/renderers/dotRenderer.ts @@ -1,6 +1,6 @@ import c from 'picocolors' import type { Task } from '../../../types' -import { clearInterval, getTests, setInterval } from '../../../utils' +import { getTests } from '../../../utils' import type { Logger } from '../../logger' export interface DotRendererOptions { diff --git a/packages/vitest/src/node/reporters/renderers/listRenderer.ts b/packages/vitest/src/node/reporters/renderers/listRenderer.ts index d9e353593ec4..8f9b9afcc2d6 100644 --- a/packages/vitest/src/node/reporters/renderers/listRenderer.ts +++ b/packages/vitest/src/node/reporters/renderers/listRenderer.ts @@ -2,7 +2,7 @@ import c from 'picocolors' import cliTruncate from 'cli-truncate' import stripAnsi from 'strip-ansi' import type { Benchmark, BenchmarkResult, SuiteHooks, Task, VitestRunMode } from '../../../types' -import { clearInterval, getTests, notNullish, setInterval } from '../../../utils' +import { getTests, notNullish } from '../../../utils' import { F_RIGHT } from '../../../utils/figures' import type { Logger } from '../../logger' import { formatProjectName, getCols, getHookStateSymbol, getStateSymbol } from './utils' @@ -59,7 +59,7 @@ function renderBenchmark(task: Benchmark, tasks: Task[]): string { return task.name const benchs = tasks - .map(i => i.type === 'benchmark' ? i.result?.benchmark : undefined) + .map(i => i.meta?.benchmark ? i.result?.benchmark : undefined) .filter(notNullish) const allItems = benchs.map(renderBenchmarkItems) @@ -120,8 +120,8 @@ export function renderTree(tasks: Task[], options: ListRendererOptions, level = name = formatFilepath(name) const padding = ' '.repeat(level) - const body = task.type === 'benchmark' - ? renderBenchmark(task, tasks) + const body = task.meta?.benchmark + ? renderBenchmark(task as Benchmark, tasks) : name output.push(padding + prefix + body + suffix) diff --git a/packages/vitest/src/node/reporters/renderers/utils.ts b/packages/vitest/src/node/reporters/renderers/utils.ts index 8d0313677da6..bb31e14ec173 100644 --- a/packages/vitest/src/node/reporters/renderers/utils.ts +++ b/packages/vitest/src/node/reporters/renderers/utils.ts @@ -132,7 +132,7 @@ export function getStateSymbol(task: Task) { } if (task.result.state === 'pass') { - return task.type === 'benchmark' + return task.meta?.benchmark ? c.green(F_DOT) : c.green(F_CHECK) } diff --git a/packages/vitest/src/node/reporters/tap-flat.ts b/packages/vitest/src/node/reporters/tap-flat.ts index 86ac59bbcaf3..125c41ebcff3 100644 --- a/packages/vitest/src/node/reporters/tap-flat.ts +++ b/packages/vitest/src/node/reporters/tap-flat.ts @@ -1,5 +1,5 @@ +import type { Task } from '@vitest/runner' import type { Vitest } from '../../node' -import type { Task } from '../../types' import { TapReporter } from './tap' function flattenTasks(task: Task, baseName = ''): Task[] { diff --git a/packages/vitest/src/node/reporters/tap.ts b/packages/vitest/src/node/reporters/tap.ts index 8bf12cdd0ac9..d3e5540a167c 100644 --- a/packages/vitest/src/node/reporters/tap.ts +++ b/packages/vitest/src/node/reporters/tap.ts @@ -1,5 +1,7 @@ +import type { Task } from '@vitest/runner' +import type { ParsedStack } from '@vitest/runner/utils' import type { Vitest } from '../../node' -import type { ParsedStack, Reporter, Task } from '../../types' +import type { Reporter } from '../../types/reporter' import { parseStacktrace } from '../../utils/source-map' import { IndentedLogger } from './renderers/indented-logger' diff --git a/packages/vitest/src/node/reporters/utils.ts b/packages/vitest/src/node/reporters/utils.ts index 0931c481e947..5a7fbc0a2f2c 100644 --- a/packages/vitest/src/node/reporters/utils.ts +++ b/packages/vitest/src/node/reporters/utils.ts @@ -1,6 +1,6 @@ import type { ViteNodeRunner } from 'vite-node/client' import type { Reporter } from '../../types' -import { ensurePackageInstalled } from '../../utils' +import { ensurePackageInstalled } from '../pkg' import { BenchmarkReportsMap, ReportersMap } from './index' import type { BenchmarkBuiltinReporters, BuiltinReporters } from './index' diff --git a/packages/vitest/src/node/reporters/verbose.ts b/packages/vitest/src/node/reporters/verbose.ts index 4fbd637ea7c4..9791cbc0175b 100644 --- a/packages/vitest/src/node/reporters/verbose.ts +++ b/packages/vitest/src/node/reporters/verbose.ts @@ -17,7 +17,7 @@ export class VerboseReporter extends DefaultReporter { for (const pack of packs) { const task = this.ctx.state.idMap.get(pack[0]) if (task && task.type === 'test' && task.result?.state && task.result?.state !== 'run') { - let title = ` ${getStateSymbol(task)} ${getFullName(task)}` + let title = ` ${getStateSymbol(task)} ${getFullName(task, c.dim(' > '))}` if (this.ctx.config.logHeapUsage && task.result.heap != null) title += c.magenta(` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`) this.ctx.logger.log(title) diff --git a/packages/vitest/src/node/sequencers/RandomSequencer.ts b/packages/vitest/src/node/sequencers/RandomSequencer.ts index c92c82f62080..d6edc463813e 100644 --- a/packages/vitest/src/node/sequencers/RandomSequencer.ts +++ b/packages/vitest/src/node/sequencers/RandomSequencer.ts @@ -1,4 +1,4 @@ -import { shuffle } from '../../utils' +import { shuffle } from '@vitest/utils' import { BaseSequencer } from './BaseSequencer' export class RandomSequencer extends BaseSequencer { diff --git a/packages/vitest/src/node/state.ts b/packages/vitest/src/node/state.ts index 54dc6ea31d40..44315fb52b8b 100644 --- a/packages/vitest/src/node/state.ts +++ b/packages/vitest/src/node/state.ts @@ -39,21 +39,7 @@ export class StateManager { return Array.from(this.errorsSet.values()) } - startCollectingPaths() { - let _resolve: CollectingPromise['resolve'] - const promise = new Promise((resolve) => { - _resolve = resolve - }) - this.collectingPromise = { promise, resolve: _resolve! } - } - - finishCollectingPaths() { - this.collectingPromise?.resolve() - this.collectingPromise = undefined - } - - async getPaths() { - await this.collectingPromise?.promise + getPaths() { return Array.from(this.pathsSet) } diff --git a/packages/vitest/src/runners.ts b/packages/vitest/src/runners.ts new file mode 100644 index 000000000000..ef5804e48c33 --- /dev/null +++ b/packages/vitest/src/runners.ts @@ -0,0 +1,2 @@ +export { VitestTestRunner } from './runtime/runners/test' +export { NodeBenchmarkRunner } from './runtime/runners/benchmark' diff --git a/packages/vitest/src/runtime/benchmark.ts b/packages/vitest/src/runtime/benchmark.ts new file mode 100644 index 000000000000..0e25180e474d --- /dev/null +++ b/packages/vitest/src/runtime/benchmark.ts @@ -0,0 +1,50 @@ +import type { TaskCustom } from '@vitest/runner' +import { getCurrentSuite } from '@vitest/runner' +import { createChainable } from '@vitest/runner/utils' +import { noop } from '@vitest/utils' +import type { BenchFunction, BenchOptions, BenchmarkAPI } from '../types' +import { isRunningInBenchmark } from '../utils' + +const benchFns = new WeakMap() +const benchOptsMap = new WeakMap() + +export function getBenchOptions(key: TaskCustom): BenchOptions { + return benchOptsMap.get(key) +} + +export function getBenchFn(key: TaskCustom): BenchFunction { + return benchFns.get(key)! +} + +export const bench = createBenchmark( + function (name, fn: BenchFunction = noop, options: BenchOptions = {}) { + if (!isRunningInBenchmark()) + throw new Error('`bench()` is only available in benchmark mode.') + + const task = getCurrentSuite().custom.call(this, name) + task.meta = { + benchmark: true, + } + benchFns.set(task, fn) + benchOptsMap.set(task, options) + }, +) + +function createBenchmark(fn: ( + ( + this: Record<'skip' | 'only' | 'todo', boolean | undefined>, + name: string, + fn?: BenchFunction, + options?: BenchOptions + ) => void +)) { + const benchmark = createChainable( + ['skip', 'only', 'todo'], + fn, + ) as BenchmarkAPI + + benchmark.skipIf = (condition: any) => (condition ? benchmark.skip : benchmark) as BenchmarkAPI + benchmark.runIf = (condition: any) => (condition ? benchmark : benchmark.skip) as BenchmarkAPI + + return benchmark as BenchmarkAPI +} diff --git a/packages/vitest/src/runtime/entry.ts b/packages/vitest/src/runtime/entry.ts index 27140c837ead..e2c8591b9950 100644 --- a/packages/vitest/src/runtime/entry.ts +++ b/packages/vitest/src/runtime/entry.ts @@ -1,13 +1,18 @@ import { promises as fs } from 'node:fs' import mm from 'micromatch' +import type { VitestRunner, VitestRunnerConstructor } from '@vitest/runner' +import { startTests } from '@vitest/runner' import type { EnvironmentOptions, ResolvedConfig, VitestEnvironment } from '../types' import { getWorkerState, resetModules } from '../utils' import { vi } from '../integrations/vi' import { envs } from '../integrations/env' -import { setupGlobalEnv, withEnv } from './setup' -import { startTests } from './run' +import { takeCoverageInsideWorker } from '../integrations/coverage' +import { setupGlobalEnv, withEnv } from './setup.node' +import { VitestTestRunner } from './runners/test' +import { NodeBenchmarkRunner } from './runners/benchmark' +import { rpc } from './rpc' -function groupBy(collection: T[], iteratee: (item: T) => K) { +function groupBy(collection: T[], iteratee: (item: T) => K) { return collection.reduce((acc, item) => { const key = iteratee(item) acc[key] ||= [] @@ -16,17 +21,56 @@ function groupBy(collection: T[], iterat }, {} as Record) } +async function getTestRunnerConstructor(config: ResolvedConfig): Promise { + if (!config.runner) + return (config.mode === 'test' ? VitestTestRunner : NodeBenchmarkRunner) as any as VitestRunnerConstructor + const mod = await import(config.runner) + if (!mod.default && typeof mod.default !== 'function') + throw new Error(`Runner must export a default function, but got ${typeof mod.default} imported from ${config.runner}`) + return mod.default as VitestRunnerConstructor +} + +async function getTestRunner(config: ResolvedConfig): Promise { + const TestRunner = await getTestRunnerConstructor(config) + const testRunner = new TestRunner(config) + + if (!testRunner.config) + testRunner.config = config + + if (!testRunner.importFile) + throw new Error('Runner must implement "importFile" method.') + + // patch some methods, so custom runners don't need to call RPC + const originalOnTaskUpdate = testRunner.onTaskUpdate + testRunner.onTaskUpdate = async (task) => { + const p = rpc().onTaskUpdate(task) + await originalOnTaskUpdate?.call(testRunner, task) + return p + } + + const originalOnCollected = testRunner.onCollected + testRunner.onCollected = async (files) => { + rpc().onCollected(files) + await originalOnCollected?.call(testRunner, files) + } + + const originalOnAfterRun = testRunner.onAfterRun + testRunner.onAfterRun = async (files) => { + const coverage = await takeCoverageInsideWorker(config.coverage) + rpc().onAfterSuiteRun({ coverage }) + await originalOnAfterRun?.call(testRunner, files) + } + + return testRunner +} + +// browser shouldn't call this! export async function run(files: string[], config: ResolvedConfig): Promise { await setupGlobalEnv(config) const workerState = getWorkerState() - // TODO @web-runner: we need to figure out how to do this on the browser - if (config.browser) { - workerState.mockMap.clear() - await startTests(files, config) - return - } + const runner = await getTestRunner(config) // if calling from a worker, there will always be one file // if calling with no-threads, this will be the whole suite @@ -91,7 +135,7 @@ export async function run(files: string[], config: ResolvedConfig): Promise globalThis.__vitest_index__ }) } - return Object.assign(context, { - __vitest_mocker__: this.mocker, - }) + return context } } diff --git a/packages/vitest/src/runtime/map.ts b/packages/vitest/src/runtime/map.ts deleted file mode 100644 index 4e9cd94efe6b..000000000000 --- a/packages/vitest/src/runtime/map.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { Awaitable, BenchFunction, BenchOptions, Benchmark, Suite, SuiteHooks, Test } from '../types' - -// use WeakMap here to make the Test and Suite object serializable -const fnMap = new WeakMap() -const hooksMap = new WeakMap() -const benchOptsMap = new WeakMap() - -export function setFn(key: Test | Benchmark, fn: (() => Awaitable) | BenchFunction) { - fnMap.set(key, fn) -} - -export function getFn(key: Task): Task extends Test ? (() => Awaitable) : BenchFunction { - return fnMap.get(key as any) -} - -export function setHooks(key: Suite, hooks: SuiteHooks) { - hooksMap.set(key, hooks) -} - -export function getHooks(key: Suite): SuiteHooks { - return hooksMap.get(key) -} - -export function isTest(task: Test | Benchmark): task is Test { - return task.type === 'test' -} - -export function setBenchOptions(key: Benchmark, val: BenchOptions) { - benchOptsMap.set(key, val) -} - -export function getBenchOptions(key: Benchmark): BenchOptions { - return benchOptsMap.get(key) -} diff --git a/packages/vitest/src/runtime/mocker.ts b/packages/vitest/src/runtime/mocker.ts index 4c9ba61e9d8d..747707242b35 100644 --- a/packages/vitest/src/runtime/mocker.ts +++ b/packages/vitest/src/runtime/mocker.ts @@ -1,7 +1,7 @@ import { existsSync, readdirSync } from 'node:fs' import { isNodeBuiltin } from 'mlly' import { basename, dirname, extname, isAbsolute, join, resolve } from 'pathe' -import c from 'picocolors' +import { getColors } from '@vitest/utils' import { getAllMockableProperties, getType, getWorkerState } from '../utils' import { distDir } from '../constants' import type { MockFactory, PendingSuiteMock } from '../types/mocker' @@ -134,6 +134,7 @@ export class VitestMocker { return target.then.bind(target) } else if (!(prop in target)) { + const c = getColors() throw new Error( `[vitest] No "${String(prop)}" export is defined on the "${mockpath}" mock. ` + 'Did you forget to return it from "vi.mock"?' diff --git a/packages/vitest/src/runtime/rpc.ts b/packages/vitest/src/runtime/rpc.ts index 56c894e362d5..796dfaef40bd 100644 --- a/packages/vitest/src/runtime/rpc.ts +++ b/packages/vitest/src/runtime/rpc.ts @@ -1,11 +1,12 @@ -import { getWorkerState } from '../utils' import { - setTimeout as safeSetTimeout, -} from '../utils/timers' + getSafeTimers, +} from '@vitest/utils' +import { getWorkerState } from '../utils' const safeRandom = Math.random function withSafeTimers(fn: () => void) { + const { setTimeout: safeSetTimeout } = getSafeTimers() const currentSetTimeout = globalThis.setTimeout const currentRandom = globalThis.Math.random diff --git a/packages/vitest/src/runtime/run.ts b/packages/vitest/src/runtime/run.ts deleted file mode 100644 index 45164b75041d..000000000000 --- a/packages/vitest/src/runtime/run.ts +++ /dev/null @@ -1,528 +0,0 @@ -import { performance } from 'perf_hooks' -import limit from 'p-limit' -import { GLOBAL_EXPECT, getState, setState } from '@vitest/expect' -import type { BenchTask, Benchmark, BenchmarkResult, File, HookCleanupCallback, HookListener, ResolvedConfig, SequenceHooks, Suite, SuiteHooks, Task, TaskResult, TaskState, Test } from '../types' -import { vi } from '../integrations/vi' -import { clearTimeout, createDefer, getFullName, getWorkerState, hasFailed, hasTests, isBrowser, isNode, isRunningInBenchmark, partitionSuiteChildren, setTimeout, shuffle } from '../utils' -import { takeCoverageInsideWorker } from '../integrations/coverage' -import type { MatcherState } from '../types/chai' -import { getSnapshotClient } from '../integrations/snapshot/chai' -import { getBenchOptions, getFn, getHooks } from './map' -import { rpc } from './rpc' -import { collectTests } from './collect' -import { processError } from './error' -import { setCurrentTest } from './test-state' - -async function importTinybench() { - if (!globalThis.EventTarget) - await import('event-target-polyfill' as any) - - return (await import('tinybench')) -} - -const now = Date.now - -function updateSuiteHookState(suite: Task, name: keyof SuiteHooks, state: TaskState) { - if (!suite.result) - suite.result = { state: 'run' } - if (!suite.result?.hooks) - suite.result.hooks = {} - const suiteHooks = suite.result.hooks - if (suiteHooks) { - suiteHooks[name] = state - updateTask(suite) - } -} - -function getSuiteHooks(suite: Suite, name: keyof SuiteHooks, sequence: SequenceHooks) { - const hooks = getHooks(suite)[name] - if (sequence === 'stack' && (name === 'afterAll' || name === 'afterEach')) - return hooks.slice().reverse() - return hooks -} - -export async function callSuiteHook( - suite: Suite, - currentTask: Task, - name: T, - args: SuiteHooks[T][0] extends HookListener ? A : never, -): Promise { - const callbacks: HookCleanupCallback[] = [] - if (name === 'beforeEach' && suite.suite) { - callbacks.push( - ...await callSuiteHook(suite.suite, currentTask, name, args), - ) - } - - updateSuiteHookState(currentTask, name, 'run') - - const state = getWorkerState() - const sequence = state.config.sequence.hooks - - const hooks = getSuiteHooks(suite, name, sequence) - - if (sequence === 'parallel') { - callbacks.push(...await Promise.all(hooks.map(fn => fn(...args as any)))) - } - else { - for (const hook of hooks) - callbacks.push(await hook(...args as any)) - } - - updateSuiteHookState(currentTask, name, 'pass') - - if (name === 'afterEach' && suite.suite) { - callbacks.push( - ...await callSuiteHook(suite.suite, currentTask, name, args), - ) - } - - return callbacks -} - -const packs = new Map() -let updateTimer: any -let previousUpdate: Promise | undefined - -function updateTask(task: Task) { - packs.set(task.id, task.result) - - clearTimeout(updateTimer) - updateTimer = setTimeout(() => { - previousUpdate = sendTasksUpdate() - }, 10) -} - -async function sendTasksUpdate() { - clearTimeout(updateTimer) - await previousUpdate - - if (packs.size) { - const p = rpc().onTaskUpdate(Array.from(packs)) - packs.clear() - return p - } -} - -const callCleanupHooks = async (cleanups: HookCleanupCallback[]) => { - await Promise.all(cleanups.map(async (fn) => { - if (typeof fn !== 'function') - return - await fn() - })) -} - -export async function runTest(test: Test) { - if (test.mode !== 'run') { - const { getSnapshotClient } = await import('../integrations/snapshot/chai') - getSnapshotClient().skipTestSnapshots(test) - return - } - - if (test.result?.state === 'fail') { - updateTask(test) - return - } - - const start = now() - - test.result = { - state: 'run', - startTime: start, - } - updateTask(test) - - clearModuleMocks() - - setCurrentTest(test) - - if (isNode) { - const { getSnapshotClient } = await import('../integrations/snapshot/chai') - await getSnapshotClient().setTest(test) - } - - const workerState = getWorkerState() - - workerState.current = test - - const retry = test.retry || 1 - for (let retryCount = 0; retryCount < retry; retryCount++) { - let beforeEachCleanups: HookCleanupCallback[] = [] - try { - setState({ - assertionCalls: 0, - isExpectingAssertions: false, - isExpectingAssertionsError: null, - expectedAssertionsNumber: null, - expectedAssertionsNumberErrorGen: null, - testPath: test.suite.file?.filepath, - currentTestName: getFullName(test), - snapshotState: getSnapshotClient().snapshotState, - }, (globalThis as any)[GLOBAL_EXPECT]) - - beforeEachCleanups = await callSuiteHook(test.suite, test, 'beforeEach', [test.context, test.suite]) - - test.result.retryCount = retryCount - - await getFn(test)() - const { - assertionCalls, - expectedAssertionsNumber, - expectedAssertionsNumberErrorGen, - isExpectingAssertions, - isExpectingAssertionsError, - // @ts-expect-error local is private - } = test.context._local - ? test.context.expect.getState() - : getState((globalThis as any)[GLOBAL_EXPECT]) - if (expectedAssertionsNumber !== null && assertionCalls !== expectedAssertionsNumber) - throw expectedAssertionsNumberErrorGen!() - if (isExpectingAssertions === true && assertionCalls === 0) - throw isExpectingAssertionsError - - test.result.state = 'pass' - } - catch (e) { - const error = processError(e) - test.result.state = 'fail' - test.result.error = error - test.result.errors = [error] - } - - try { - await callSuiteHook(test.suite, test, 'afterEach', [test.context, test.suite]) - await callCleanupHooks(beforeEachCleanups) - } - catch (e) { - const error = processError(e) - test.result.state = 'fail' - test.result.error = error - test.result.errors = [error] - } - - if (test.result.state === 'pass') - break - - // update retry info - updateTask(test) - } - - if (test.result.state === 'fail') - await Promise.all(test.onFailed?.map(fn => fn(test.result!)) || []) - - // if test is marked to be failed, flip the result - if (test.fails) { - if (test.result.state === 'pass') { - const error = processError(new Error('Expect test to fail')) - test.result.state = 'fail' - test.result.error = error - test.result.errors = [error] - } - else { - test.result.state = 'pass' - test.result.error = undefined - test.result.errors = undefined - } - } - - if (isBrowser && test.result.error) - console.error(test.result.error.message, test.result.error.stackStr) - - setCurrentTest(undefined) - - if (isNode) { - const { getSnapshotClient } = await import('../integrations/snapshot/chai') - getSnapshotClient().clearTest() - } - - test.result.duration = now() - start - - if (workerState.config.logHeapUsage && isNode) - test.result.heap = process.memoryUsage().heapUsed - - workerState.current = undefined - - updateTask(test) -} - -function markTasksAsSkipped(suite: Suite) { - suite.tasks.forEach((t) => { - t.mode = 'skip' - t.result = { ...t.result, state: 'skip' } - updateTask(t) - if (t.type === 'suite') - markTasksAsSkipped(t) - }) -} - -export async function runSuite(suite: Suite) { - if (suite.result?.state === 'fail') { - markTasksAsSkipped(suite) - updateTask(suite) - return - } - - const start = now() - - suite.result = { - state: 'run', - startTime: start, - } - - updateTask(suite) - - const workerState = getWorkerState() - - if (suite.mode === 'skip') { - suite.result.state = 'skip' - } - else if (suite.mode === 'todo') { - suite.result.state = 'todo' - } - else { - try { - const beforeAllCleanups = await callSuiteHook(suite, suite, 'beforeAll', [suite]) - - if (isRunningInBenchmark()) { - await runBenchmarkSuite(suite) - } - else { - for (let tasksGroup of partitionSuiteChildren(suite)) { - if (tasksGroup[0].concurrent === true) { - const mutex = limit(workerState.config.maxConcurrency) - await Promise.all(tasksGroup.map(c => mutex(() => runSuiteChild(c)))) - } - else { - const { sequence } = workerState.config - if (sequence.shuffle || suite.shuffle) { - // run describe block independently from tests - const suites = tasksGroup.filter(group => group.type === 'suite') - const tests = tasksGroup.filter(group => group.type === 'test') - const groups = shuffle([suites, tests], sequence.seed) - tasksGroup = groups.flatMap(group => shuffle(group, sequence.seed)) - } - for (const c of tasksGroup) - await runSuiteChild(c) - } - } - } - - await callSuiteHook(suite, suite, 'afterAll', [suite]) - await callCleanupHooks(beforeAllCleanups) - } - catch (e) { - const error = processError(e) - suite.result.state = 'fail' - suite.result.error = error - suite.result.errors = [error] - } - } - suite.result.duration = now() - start - - if (workerState.config.logHeapUsage && isNode) - suite.result.heap = process.memoryUsage().heapUsed - - if (suite.mode === 'run') { - if (!hasTests(suite)) { - suite.result.state = 'fail' - if (!suite.result.error) { - const error = processError(new Error(`No test found in suite ${suite.name}`)) - suite.result.error = error - suite.result.errors = [error] - } - } - else if (hasFailed(suite)) { - suite.result.state = 'fail' - } - else { - suite.result.state = 'pass' - } - } - - updateTask(suite) -} - -function createBenchmarkResult(name: string): BenchmarkResult { - return { - name, - rank: 0, - rme: 0, - samples: [] as number[], - } as BenchmarkResult -} - -async function runBenchmarkSuite(suite: Suite) { - const { Task, Bench } = await importTinybench() - const start = performance.now() - - const benchmarkGroup = [] - const benchmarkSuiteGroup = [] - for (const task of suite.tasks) { - if (task.mode !== 'run') - continue - - if (task.type === 'benchmark') - benchmarkGroup.push(task) - else if (task.type === 'suite') - benchmarkSuiteGroup.push(task) - } - - if (benchmarkSuiteGroup.length) - await Promise.all(benchmarkSuiteGroup.map(subSuite => runBenchmarkSuite(subSuite))) - - if (benchmarkGroup.length) { - const defer = createDefer() - const benchmarkMap: Record = {} - suite.result = { - state: 'run', - startTime: start, - benchmark: createBenchmarkResult(suite.name), - } - updateTask(suite) - benchmarkGroup.forEach((benchmark, idx) => { - const options = getBenchOptions(benchmark) - const benchmarkInstance = new Bench(options) - - const benchmarkFn = getFn(benchmark) - - benchmark.result = { - state: 'run', - startTime: start, - benchmark: createBenchmarkResult(benchmark.name), - } - const id = idx.toString() - benchmarkMap[id] = benchmark - - const task = new Task(benchmarkInstance, id, benchmarkFn) - benchmark.task = task - updateTask(benchmark) - }) - - benchmarkGroup.forEach((benchmark) => { - benchmark.task!.addEventListener('complete', (e) => { - const task = e.task - const _benchmark = benchmarkMap[task.name || ''] - if (_benchmark) { - const taskRes = task.result! - const result = _benchmark.result!.benchmark! - Object.assign(result, taskRes) - updateTask(_benchmark) - } - }) - benchmark.task!.addEventListener('error', (e) => { - const task = e.task - const _benchmark = benchmarkMap[task.name || ''] - defer.reject(_benchmark ? task.result!.error : e) - }) - }) - - Promise.all(benchmarkGroup.map(async (benchmark) => { - await benchmark.task!.warmup() - return await new Promise(resolve => setTimeout(async () => { - resolve(await benchmark.task!.run()) - })) - })).then((tasks) => { - suite.result!.duration = performance.now() - start - suite.result!.state = 'pass' - - tasks - .sort((a, b) => a.result!.mean - b.result!.mean) - .forEach((cycle, idx) => { - const benchmark = benchmarkMap[cycle.name || ''] - benchmark.result!.state = 'pass' - if (benchmark) { - const result = benchmark.result!.benchmark! - result.rank = Number(idx) + 1 - updateTask(benchmark) - } - }) - updateTask(suite) - defer.resolve(null) - }) - - await defer - } -} - -async function runSuiteChild(c: Task) { - if (c.type === 'test') - return runTest(c) - - else if (c.type === 'suite') - return runSuite(c) -} - -async function runSuites(suites: Suite[]) { - for (const suite of suites) - await runSuite(suite) -} - -export async function runFiles(files: File[], config: ResolvedConfig) { - for (const file of files) { - if (!file.tasks.length && !config.passWithNoTests) { - if (!file.result?.errors?.length) { - const error = processError(new Error(`No test suite found in file ${file.filepath}`)) - file.result = { - state: 'fail', - error, - errors: [error], - } - } - } - await runSuite(file) - } -} - -async function startTestsBrowser(paths: string[], config: ResolvedConfig) { - if (isNode) { - rpc().onPathsCollected(paths) - } - else { - const files = await collectTests(paths, config) - await rpc().onCollected(files) - await runSuites(files) - await sendTasksUpdate() - } -} - -async function startTestsNode(paths: string[], config: ResolvedConfig) { - const files = await collectTests(paths, config) - - rpc().onCollected(files) - - const { getSnapshotClient } = await import('../integrations/snapshot/chai') - getSnapshotClient().clear() - - await runFiles(files, config) - - const coverage = await takeCoverageInsideWorker(config.coverage) - rpc().onAfterSuiteRun({ coverage }) - - await getSnapshotClient().saveCurrent() - - await sendTasksUpdate() -} - -export async function startTests(paths: string[], config: ResolvedConfig) { - if (config.browser) - return startTestsBrowser(paths, config) - else - return startTestsNode(paths, config) -} - -export function clearModuleMocks() { - const { clearMocks, mockReset, restoreMocks, unstubEnvs, unstubGlobals } = getWorkerState().config - - // since each function calls another, we can just call one - if (restoreMocks) - vi.restoreAllMocks() - else if (mockReset) - vi.resetAllMocks() - else if (clearMocks) - vi.clearAllMocks() - - if (unstubEnvs) - vi.unstubAllEnvs() - if (unstubGlobals) - vi.unstubAllGlobals() -} diff --git a/packages/vitest/src/runtime/runners/benchmark.ts b/packages/vitest/src/runtime/runners/benchmark.ts new file mode 100644 index 000000000000..9dfe2a8d74af --- /dev/null +++ b/packages/vitest/src/runtime/runners/benchmark.ts @@ -0,0 +1,139 @@ +import type { Suite, Task, VitestRunner, VitestRunnerImportSource } from '@vitest/runner' +import { updateTask as updateRunnerTask } from '@vitest/runner' +import { createDefer, getSafeTimers } from '@vitest/utils' +import { getBenchFn, getBenchOptions } from '../benchmark' +import { getWorkerState } from '../../utils' +import type { BenchTask, Benchmark, BenchmarkResult } from '../../types/benchmark' +import type { ResolvedConfig } from '../../types/config' + +async function importTinybench() { + if (!globalThis.EventTarget) + await import('event-target-polyfill' as any) + + return (await import('tinybench')) +} + +function createBenchmarkResult(name: string): BenchmarkResult { + return { + name, + rank: 0, + rme: 0, + samples: [] as number[], + } as BenchmarkResult +} + +async function runBenchmarkSuite(suite: Suite, runner: VitestRunner) { + const { Task, Bench } = await importTinybench() + const start = performance.now() + + const benchmarkGroup: Benchmark[] = [] + const benchmarkSuiteGroup = [] + for (const task of suite.tasks) { + if (task.mode !== 'run') + continue + + if (task.meta?.benchmark) + benchmarkGroup.push(task as Benchmark) + else if (task.type === 'suite') + benchmarkSuiteGroup.push(task) + } + + if (benchmarkSuiteGroup.length) + await Promise.all(benchmarkSuiteGroup.map(subSuite => runBenchmarkSuite(subSuite, runner))) + + if (benchmarkGroup.length) { + const defer = createDefer() + const benchmarkMap: Record = {} + suite.result = { + state: 'run', + startTime: start, + benchmark: createBenchmarkResult(suite.name), + } + updateTask(suite) + benchmarkGroup.forEach((benchmark, idx) => { + const options = getBenchOptions(benchmark) + const benchmarkInstance = new Bench(options) + + const benchmarkFn = getBenchFn(benchmark) + + benchmark.result = { + state: 'run', + startTime: start, + benchmark: createBenchmarkResult(benchmark.name), + } + const id = idx.toString() + benchmarkMap[id] = benchmark + + const task = new Task(benchmarkInstance, id, benchmarkFn) + benchmark.meta.task = task + updateTask(benchmark) + }) + + benchmarkGroup.forEach((benchmark) => { + benchmark.meta.task!.addEventListener('complete', (e) => { + const task = e.task + const _benchmark = benchmarkMap[task.name || ''] + if (_benchmark) { + const taskRes = task.result! + const result = _benchmark.result!.benchmark! + Object.assign(result, taskRes) + updateTask(_benchmark) + } + }) + benchmark.meta.task!.addEventListener('error', (e) => { + const task = e.task + const _benchmark = benchmarkMap[task.name || ''] + defer.reject(_benchmark ? task.result!.error : e) + }) + }) + + Promise.all(benchmarkGroup.map(async (benchmark) => { + await benchmark.meta.task!.warmup() + const { setTimeout } = getSafeTimers() + return await new Promise(resolve => setTimeout(async () => { + resolve(await benchmark.meta.task!.run()) + })) + })).then((tasks) => { + suite.result!.duration = performance.now() - start + suite.result!.state = 'pass' + + tasks + .sort((a, b) => a.result!.mean - b.result!.mean) + .forEach((cycle, idx) => { + const benchmark = benchmarkMap[cycle.name || ''] + benchmark.result!.state = 'pass' + if (benchmark) { + const result = benchmark.result!.benchmark! + result.rank = Number(idx) + 1 + updateTask(benchmark) + } + }) + updateTask(suite) + defer.resolve(null) + }) + + await defer + } + + function updateTask(task: Task) { + updateRunnerTask(task, runner) + } +} + +export class NodeBenchmarkRunner implements VitestRunner { + constructor(public config: ResolvedConfig) {} + + importFile(filepath: string, source: VitestRunnerImportSource): unknown { + if (source === 'setup') + getWorkerState().moduleCache.delete(filepath) + return import(filepath) + } + + async runSuite(suite: Suite): Promise { + await runBenchmarkSuite(suite, this) + } + + async runTest(): Promise { + throw new Error('`test()` and `it()` is only available in test mode.') + } +} diff --git a/packages/vitest/src/runtime/runners/test.ts b/packages/vitest/src/runtime/runners/test.ts new file mode 100644 index 000000000000..ca41f1f45970 --- /dev/null +++ b/packages/vitest/src/runtime/runners/test.ts @@ -0,0 +1,118 @@ +import type { Suite, Test, TestContext, VitestRunner, VitestRunnerImportSource } from '@vitest/runner' +import { GLOBAL_EXPECT, getState, setState } from '@vitest/expect' +import { getSnapshotClient } from '../../integrations/snapshot/chai' +import { vi } from '../../integrations/vi' +import { getFullName, getWorkerState } from '../../utils' +import { createExpect } from '../../integrations/chai/index' +import type { ResolvedConfig } from '../../types/config' + +export class VitestTestRunner implements VitestRunner { + private snapshotClient = getSnapshotClient() + private workerState = getWorkerState() + + constructor(public config: ResolvedConfig) {} + + importFile(filepath: string, source: VitestRunnerImportSource): unknown { + if (source === 'setup') + this.workerState.moduleCache.delete(filepath) + return import(filepath) + } + + onBeforeRun() { + this.snapshotClient.clear() + } + + async onAfterRun() { + await this.snapshotClient.saveCurrent() + } + + onAfterRunSuite(suite: Suite) { + if (this.config.logHeapUsage && typeof process !== 'undefined') + suite.result!.heap = process.memoryUsage().heapUsed + } + + onAfterRunTest(test: Test) { + this.snapshotClient.clearTest() + + if (this.config.logHeapUsage && typeof process !== 'undefined') + test.result!.heap = process.memoryUsage().heapUsed + + this.workerState.current = undefined + } + + async onBeforeRunTest(test: Test) { + if (test.mode !== 'run') { + this.snapshotClient.skipTestSnapshots(test) + return + } + + clearModuleMocks(this.config) + await this.snapshotClient.setTest(test) + + this.workerState.current = test + } + + onBeforeTryTest(test: Test) { + setState({ + assertionCalls: 0, + isExpectingAssertions: false, + isExpectingAssertionsError: null, + expectedAssertionsNumber: null, + expectedAssertionsNumberErrorGen: null, + testPath: test.suite.file?.filepath, + currentTestName: getFullName(test), + snapshotState: this.snapshotClient.snapshotState, + }, (globalThis as any)[GLOBAL_EXPECT]) + } + + onAfterTryTest(test: Test) { + const { + assertionCalls, + expectedAssertionsNumber, + expectedAssertionsNumberErrorGen, + isExpectingAssertions, + isExpectingAssertionsError, + // @ts-expect-error local is untyped + } = test.context._local + ? test.context.expect.getState() + : getState((globalThis as any)[GLOBAL_EXPECT]) + if (expectedAssertionsNumber !== null && assertionCalls !== expectedAssertionsNumber) + throw expectedAssertionsNumberErrorGen!() + if (isExpectingAssertions === true && assertionCalls === 0) + throw isExpectingAssertionsError + } + + extendTestContext(context: TestContext): TestContext { + let _expect: Vi.ExpectStatic | undefined + Object.defineProperty(context, 'expect', { + get() { + if (!_expect) + _expect = createExpect(context.meta) + return _expect + }, + }) + Object.defineProperty(context, '_local', { + get() { + return _expect != null + }, + }) + return context + } +} + +function clearModuleMocks(config: ResolvedConfig) { + const { clearMocks, mockReset, restoreMocks, unstubEnvs, unstubGlobals } = config + + // since each function calls another, we can just call one + if (restoreMocks) + vi.restoreAllMocks() + else if (mockReset) + vi.resetAllMocks() + else if (clearMocks) + vi.clearAllMocks() + + if (unstubEnvs) + vi.unstubAllEnvs() + if (unstubGlobals) + vi.unstubAllGlobals() +} diff --git a/packages/vitest/src/runtime/setup.common.ts b/packages/vitest/src/runtime/setup.common.ts new file mode 100644 index 000000000000..32d8f5fc2096 --- /dev/null +++ b/packages/vitest/src/runtime/setup.common.ts @@ -0,0 +1,23 @@ +import { setSafeTimers } from '@vitest/utils' +import { resetRunOnceCounter } from '../integrations/run-once' +import type { ResolvedConfig } from '../types' + +let globalSetup = false +export async function setupCommonEnv(config: ResolvedConfig) { + resetRunOnceCounter() + setupDefines(config.defines) + + if (globalSetup) + return + + globalSetup = true + setSafeTimers() + + if (config.globals) + (await import('../integrations/globals')).registerApiGlobally() +} + +function setupDefines(defines: Record) { + for (const key in defines) + (globalThis as any)[key] = defines[key] +} diff --git a/packages/vitest/src/runtime/setup.ts b/packages/vitest/src/runtime/setup.node.ts similarity index 82% rename from packages/vitest/src/runtime/setup.ts rename to packages/vitest/src/runtime/setup.node.ts index 40dddf244177..b7494cad5a6d 100644 --- a/packages/vitest/src/runtime/setup.ts +++ b/packages/vitest/src/runtime/setup.node.ts @@ -1,55 +1,48 @@ -/* eslint-disable n/no-deprecated-api */ - +import { createRequire } from 'node:module' +import p from 'picocolors' import { installSourcemapsSupport } from 'vite-node/source-map' +import { setColors } from '@vitest/utils' import { environments } from '../integrations/env' import type { Environment, ResolvedConfig } from '../types' -import { clearTimeout, getWorkerState, isNode, setTimeout, toArray } from '../utils' +import { getSafeTimers, getWorkerState } from '../utils' import * as VitestIndex from '../index' -import { resetRunOnceCounter } from '../integrations/run-once' import { RealDate } from '../integrations/mock/date' import { expect } from '../integrations/chai' +import { setupSnapshotEnvironment } from '../integrations/snapshot/env' +import { NodeSnapshotEnvironment } from '../integrations/snapshot/environments/node' import { rpc } from './rpc' +import { setupCommonEnv } from './setup.common' +// this should only be used in Node let globalSetup = false export async function setupGlobalEnv(config: ResolvedConfig) { - resetRunOnceCounter() + await setupCommonEnv(config) Object.defineProperty(globalThis, '__vitest_index__', { value: VitestIndex, enumerable: false, }) - // should be re-declared for each test - // if run with "threads: false" - setupDefines(config.defines) - if (globalSetup) return + globalSetup = true + setupSnapshotEnvironment(new NodeSnapshotEnvironment()) + setColors(p) + + const require = createRequire(import.meta.url) // always mock "required" `css` files, because we cannot process them require.extensions['.css'] = () => ({}) require.extensions['.scss'] = () => ({}) require.extensions['.sass'] = () => ({}) - globalSetup = true - - if (isNode) { - const state = getWorkerState() - - installSourcemapsSupport({ - getSourceMap: source => state.moduleCache.getSourceMap(source), - }) - - await setupConsoleLogSpy() - } + const state = getWorkerState() - if (config.globals) - (await import('../integrations/globals')).registerApiGlobally() -} + installSourcemapsSupport({ + getSourceMap: source => state.moduleCache.getSourceMap(source), + }) -function setupDefines(defines: Record) { - for (const key in defines) - (globalThis as any)[key] = defines[key] + await setupConsoleLogSpy() } export async function setupConsoleLogSpy() { @@ -60,6 +53,7 @@ export async function setupConsoleLogSpy() { const { Writable } = await import('node:stream') const { Console } = await import('node:console') + const { setTimeout, clearTimeout } = getSafeTimers() // group sync console.log calls with macro task function schedule(taskId: string) { @@ -194,13 +188,3 @@ export async function withEnv( await env.teardown(globalThis) } } - -export async function runSetupFiles(config: ResolvedConfig) { - const files = toArray(config.setupFiles) - await Promise.all( - files.map(async (fsPath) => { - getWorkerState().moduleCache.delete(fsPath) - await import(fsPath) - }), - ) -} diff --git a/packages/vitest/src/runtime/utils.ts b/packages/vitest/src/runtime/utils.ts deleted file mode 100644 index 151745563aeb..000000000000 --- a/packages/vitest/src/runtime/utils.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { DoneCallback } from '../types' - -/** - * A simple wrapper for converting callback style to promise - */ -export function withCallback(fn: (done: DoneCallback) => void): Promise { - return new Promise((resolve, reject) => - fn((err) => { - if (err) - reject(err) - else - resolve() - }), - ) -} diff --git a/packages/vitest/src/runtime/worker.ts b/packages/vitest/src/runtime/worker.ts index e769f5da1f40..c21c2b5545c8 100644 --- a/packages/vitest/src/runtime/worker.ts +++ b/packages/vitest/src/runtime/worker.ts @@ -1,6 +1,7 @@ import { relative, resolve } from 'pathe' import { createBirpc } from 'birpc' import { workerId as poolId } from 'tinypool' +import { processError } from '@vitest/runner/utils' import { ModuleCacheMap } from 'vite-node/client' import { isPrimitive } from 'vite-node/utils' import type { ResolvedConfig, WorkerContext, WorkerRPC } from '../types' @@ -9,7 +10,6 @@ import { getWorkerState } from '../utils' import type { MockMap } from '../types/mocker' import { executeInViteNode } from './execute' import { rpc } from './rpc' -import { processError } from './error' let _viteNode: { run: (files: string[], config: ResolvedConfig) => Promise diff --git a/packages/vitest/src/suite.ts b/packages/vitest/src/suite.ts new file mode 100644 index 000000000000..c2f67b8dfe7f --- /dev/null +++ b/packages/vitest/src/suite.ts @@ -0,0 +1,2 @@ +export { getCurrentSuite, getFn, setFn } from '@vitest/runner' +export { createChainable } from '@vitest/runner/utils' diff --git a/packages/vitest/src/typecheck/collect.ts b/packages/vitest/src/typecheck/collect.ts index 0916b499be13..a30f80cc47f7 100644 --- a/packages/vitest/src/typecheck/collect.ts +++ b/packages/vitest/src/typecheck/collect.ts @@ -3,8 +3,8 @@ import { parse as parseAst } from 'acorn' import { ancestor as walkAst } from 'acorn-walk' import type { RawSourceMap } from 'vite-node' +import { calculateSuiteHash, generateHash, interpretTaskModes, someTasksAreOnly } from '@vitest/runner/utils' import type { File, Suite, Test, Vitest } from '../types' -import { calculateSuiteHash, generateHash, interpretTaskModes, someTasksAreOnly } from '../utils/collect' interface ParsedFile extends File { start: number diff --git a/packages/vitest/src/typecheck/typechecker.ts b/packages/vitest/src/typecheck/typechecker.ts index c8590b03193e..20d68fec0973 100644 --- a/packages/vitest/src/typecheck/typechecker.ts +++ b/packages/vitest/src/typecheck/typechecker.ts @@ -3,7 +3,8 @@ import type { ExecaChildProcess } from 'execa' import { execa } from 'execa' import { extname, resolve } from 'pathe' import { SourceMapConsumer } from 'source-map' -import { ensurePackageInstalled, getTasks } from '../utils' +import { getTasks } from '../utils' +import { ensurePackageInstalled } from '../node/pkg' import type { Awaitable, File, ParsedStack, Task, TaskResultPack, TaskState, TscErrorInfo, Vitest } from '../types' import { getRawErrsMapFromTsCompile, getTsconfig } from './parse' import { createIndexMap } from './utils' diff --git a/packages/vitest/src/types/benchmark.ts b/packages/vitest/src/types/benchmark.ts index c114e258d2bf..03a9bfc40b68 100644 --- a/packages/vitest/src/types/benchmark.ts +++ b/packages/vitest/src/types/benchmark.ts @@ -1,7 +1,9 @@ +import type { TaskCustom } from '@vitest/runner' +import type { ChainableFunction } from '@vitest/runner/utils' +import type { Arrayable } from '@vitest/utils' import type { Bench as BenchFactory, Options as BenchOptions, Task as BenchTask, TaskResult as BenchTaskResult, TaskResult as TinybenchResult } from 'tinybench' import type { BenchmarkBuiltinReporters } from '../node/reporters' -import type { ChainableFunction } from '../runtime/chain' -import type { Arrayable, Reporter, Suite, TaskBase, TaskResult } from '.' +import type { Reporter } from './reporter' export interface BenchmarkUserOptions { /** @@ -37,12 +39,12 @@ export interface BenchmarkUserOptions { outputFile?: string | (Partial> & Record) } -export interface Benchmark extends TaskBase { - type: 'benchmark' - suite: Suite - result?: TaskResult - fails?: boolean - task?: BenchTask +export interface Benchmark extends TaskCustom { + meta: { + benchmark: true + task?: BenchTask + result?: BenchTaskResult + } } export interface BenchmarkResult extends TinybenchResult { diff --git a/packages/vitest/src/types/chai.ts b/packages/vitest/src/types/chai.ts index 94069cfe9162..a922590e82b9 100644 --- a/packages/vitest/src/types/chai.ts +++ b/packages/vitest/src/types/chai.ts @@ -1,8 +1 @@ -import type { MatcherState as JestMatcherState } from '@vitest/expect' -import type SnapshotState from '../integrations/snapshot/port/state' -import type { VitestEnvironment } from './config' - -export interface MatcherState extends JestMatcherState { - environment: VitestEnvironment - snapshotState: SnapshotState -} +export { MatcherState } from '@vitest/expect' diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index 1cf90d4ccf4f..376654d606ec 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -514,6 +514,11 @@ export interface InlineConfig { * @default 300 */ slowTestThreshold?: number + + /** + * Path to a custom test runner. + */ + runner?: string } export interface TypecheckConfig { @@ -584,7 +589,7 @@ export interface UserConfig extends InlineConfig { shard?: string } -export interface ResolvedConfig extends Omit, 'config' | 'filters' | 'coverage' | 'testNamePattern' | 'related' | 'api' | 'reporters' | 'resolveSnapshotPath' | 'benchmark' | 'shard' | 'cache' | 'sequence' | 'typecheck'> { +export interface ResolvedConfig extends Omit, 'config' | 'filters' | 'coverage' | 'testNamePattern' | 'related' | 'api' | 'reporters' | 'resolveSnapshotPath' | 'benchmark' | 'shard' | 'cache' | 'sequence' | 'typecheck' | 'runner'> { mode: VitestRunMode base?: string @@ -624,6 +629,7 @@ export interface ResolvedConfig extends Omit, 'config' | 'f } typecheck: TypecheckConfig + runner?: string } export type RuntimeConfig = Pick< diff --git a/packages/vitest/src/types/general.ts b/packages/vitest/src/types/general.ts index f51be6877839..d2eb8c8cc851 100644 --- a/packages/vitest/src/types/general.ts +++ b/packages/vitest/src/types/general.ts @@ -1,3 +1,5 @@ +export type { ErrorWithDiff, ParsedStack } from '@vitest/runner/utils' + export type Awaitable = T | PromiseLike export type Nullable = T | null | undefined export type Arrayable = T | Array @@ -47,27 +49,6 @@ export interface UserConsoleLog { size: number } -export interface ParsedStack { - method: string - file: string - line: number - column: number -} - -export interface ErrorWithDiff extends Error { - name: string - nameStr?: string - stack?: string - stackStr?: string - stacks?: ParsedStack[] - showDiff?: boolean - actual?: any - expected?: any - operator?: string - type?: string - frame?: string -} - export interface ModuleGraphData { graph: Record externalized: string[] diff --git a/packages/vitest/src/types/global.ts b/packages/vitest/src/types/global.ts index a1b87d37dae6..bfcd580dfef2 100644 --- a/packages/vitest/src/types/global.ts +++ b/packages/vitest/src/types/global.ts @@ -1,7 +1,10 @@ import type { Plugin as PrettyFormatPlugin } from 'pretty-format' import type { MatchersObject } from '@vitest/expect' +import type SnapshotState from '../integrations/snapshot/port/state' import type { MatcherState } from './chai' -import type { Constructable } from './general' +import type { Constructable, UserConsoleLog } from './general' +import type { VitestEnvironment } from './config' +import type { BenchmarkResult } from './benchmark' type Promisify = { [K in keyof O]: O[K] extends (...args: infer A) => infer R @@ -11,6 +14,27 @@ type Promisify = { : O[K] } +declare module '@vitest/expect' { + interface MatcherState { + environment: VitestEnvironment + snapshotState: SnapshotState + } +} + +declare module '@vitest/runner' { + interface TestContext { + expect: Vi.ExpectStatic + } + + interface TaskBase { + logs?: UserConsoleLog[] + } + + interface TaskResult { + benchmark?: BenchmarkResult + } +} + declare global { // support augmenting jest.Matchers by other libraries namespace jest { diff --git a/packages/vitest/src/types/tasks.ts b/packages/vitest/src/types/tasks.ts index da62441943ce..d1fcdc66232e 100644 --- a/packages/vitest/src/types/tasks.ts +++ b/packages/vitest/src/types/tasks.ts @@ -1,234 +1,24 @@ -import type { ChainableFunction } from '../runtime/chain' -import type { BenchFactory, Benchmark, BenchmarkAPI, BenchmarkResult } from './benchmark' -import type { Awaitable, ErrorWithDiff, UserConsoleLog } from './general' - -export type RunMode = 'run' | 'skip' | 'only' | 'todo' -export type TaskState = RunMode | 'pass' | 'fail' - -export interface TaskBase { - id: string - name: string - mode: RunMode - concurrent?: boolean - shuffle?: boolean - suite?: Suite - file?: File - result?: TaskResult - retry?: number - logs?: UserConsoleLog[] - meta?: any -} - -export interface TaskResult { - state: TaskState - duration?: number - startTime?: number - heap?: number - /** - * @deprecated Use "errors" instead - */ - error?: ErrorWithDiff - errors?: ErrorWithDiff[] - htmlError?: string - hooks?: Partial> - benchmark?: BenchmarkResult - retryCount?: number -} - -export type TaskResultPack = [id: string, result: TaskResult | undefined] - -export interface Suite extends TaskBase { - type: 'suite' - tasks: Task[] - filepath?: string - benchmark?: BenchFactory - projectName?: string -} - -export interface File extends Suite { - filepath: string - collectDuration?: number - setupDuration?: number -} - -export interface Test extends TaskBase { - type: 'test' - suite: Suite - result?: TaskResult - fails?: boolean - context: TestContext & ExtraContext - onFailed?: OnTestFailedHandler[] -} - -export type Task = Test | Suite | File | Benchmark - -export type DoneCallback = (error?: any) => void -export type TestFunction = (context: TestContext & ExtraContext) => Awaitable | void - -// jest's ExtractEachCallbackArgs -type ExtractEachCallbackArgs> = { - 1: [T[0]] - 2: [T[0], T[1]] - 3: [T[0], T[1], T[2]] - 4: [T[0], T[1], T[2], T[3]] - 5: [T[0], T[1], T[2], T[3], T[4]] - 6: [T[0], T[1], T[2], T[3], T[4], T[5]] - 7: [T[0], T[1], T[2], T[3], T[4], T[5], T[6]] - 8: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7]] - 9: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8]] - 10: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8], T[9]] - fallback: Array ? U : any> -}[T extends Readonly<[any]> - ? 1 - : T extends Readonly<[any, any]> - ? 2 - : T extends Readonly<[any, any, any]> - ? 3 - : T extends Readonly<[any, any, any, any]> - ? 4 - : T extends Readonly<[any, any, any, any, any]> - ? 5 - : T extends Readonly<[any, any, any, any, any, any]> - ? 6 - : T extends Readonly<[any, any, any, any, any, any, any]> - ? 7 - : T extends Readonly<[any, any, any, any, any, any, any, any]> - ? 8 - : T extends Readonly<[any, any, any, any, any, any, any, any, any]> - ? 9 - : T extends Readonly<[any, any, any, any, any, any, any, any, any, any]> - ? 10 - : 'fallback'] - -interface SuiteEachFunction { - (cases: ReadonlyArray): ( - name: string, - fn: (...args: T) => Awaitable, - ) => void - >(cases: ReadonlyArray): ( - name: string, - fn: (...args: ExtractEachCallbackArgs) => Awaitable, - ) => void - (cases: ReadonlyArray): ( - name: string, - fn: (...args: T[]) => Awaitable, - ) => void -} - -interface TestEachFunction { - (cases: ReadonlyArray): ( - name: string, - fn: (...args: T) => Awaitable, - options?: number | TestOptions, - ) => void - >(cases: ReadonlyArray): ( - name: string, - fn: (...args: ExtractEachCallbackArgs) => Awaitable, - options?: number | TestOptions, - ) => void - (cases: ReadonlyArray): ( - name: string, - fn: (...args: T[]) => Awaitable, - options?: number | TestOptions, - ) => void - (...args: [TemplateStringsArray, ...any]): ( - name: string, - fn: (...args: any[]) => Awaitable, - options?: number | TestOptions, - ) => void -} - -type ChainableTestAPI = ChainableFunction< - 'concurrent' | 'only' | 'skip' | 'todo' | 'fails', - [name: string, fn?: TestFunction, options?: number | TestOptions], - void, - { - each: TestEachFunction - (name: string, fn?: TestFunction, options?: number | TestOptions): void - } -> - -export interface TestOptions { - /** - * Test timeout. - */ - timeout?: number - /** - * Times to retry the test if fails. Useful for making flaky tests more stable. - * When retries is up, the last test error will be thrown. - * - * @default 1 - */ - retry?: number -} - -export type TestAPI = ChainableTestAPI & { - each: TestEachFunction - skipIf(condition: any): ChainableTestAPI - runIf(condition: any): ChainableTestAPI -} - -type ChainableSuiteAPI = ChainableFunction< - 'concurrent' | 'only' | 'skip' | 'todo' | 'shuffle', - [name: string, factory?: SuiteFactory, options?: number | TestOptions], - SuiteCollector, - { - each: TestEachFunction - (name: string, factory?: SuiteFactory): SuiteCollector - } -> - -export type SuiteAPI = ChainableSuiteAPI & { - each: SuiteEachFunction - skipIf(condition: any): ChainableSuiteAPI - runIf(condition: any): ChainableSuiteAPI -} - -export type HookListener = (...args: T) => Awaitable - -export type HookCleanupCallback = (() => Awaitable) | void - -export interface SuiteHooks { - beforeAll: HookListener<[Suite | File], HookCleanupCallback>[] - afterAll: HookListener<[Suite | File]>[] - beforeEach: HookListener<[TestContext & ExtraContext, Suite], HookCleanupCallback>[] - afterEach: HookListener<[TestContext & ExtraContext, Suite]>[] -} - -export interface SuiteCollector { - readonly name: string - readonly mode: RunMode - type: 'collector' - test: TestAPI - benchmark: BenchmarkAPI - tasks: (Suite | Test | Benchmark | SuiteCollector)[] - collect: (file?: File) => Promise - clear: () => void - on: >(name: T, ...fn: SuiteHooks[T]) => void -} - -export type SuiteFactory = (test: (name: string, fn: TestFunction) => void) => Awaitable - -export interface RuntimeContext { - tasks: (SuiteCollector | Test)[] - currentSuite: SuiteCollector | null -} - -export interface TestContext { - /** - * Metadata of the current test - */ - meta: Readonly - - /** - * A expect instance bound to the test - */ - expect: Vi.ExpectStatic - - /** - * Extract hooks on test failed - */ - onTestFailed: (fn: OnTestFailedHandler) => void -} - -export type OnTestFailedHandler = (result: TaskResult) => Awaitable +export type { + RunMode, + TaskState, + TaskBase, + TaskResult, + TaskResultPack, + Suite, + File, + Test, + Task, + DoneCallback, + TestFunction, + TestOptions, + TestAPI, + SuiteAPI, + HookListener, + HookCleanupCallback, + SuiteHooks, + SuiteCollector, + SuiteFactory, + RuntimeContext, + TestContext, + OnTestFailedHandler, +} from '@vitest/runner/types' diff --git a/packages/vitest/src/types/worker.ts b/packages/vitest/src/types/worker.ts index a4eb92dde048..deff9cfaacf3 100644 --- a/packages/vitest/src/types/worker.ts +++ b/packages/vitest/src/types/worker.ts @@ -1,9 +1,9 @@ import type { MessagePort } from 'node:worker_threads' +import type { File, TaskResultPack, Test } from '@vitest/runner' import type { FetchFunction, ModuleCacheMap, RawSourceMap, ViteNodeResolveId } from 'vite-node' import type { BirpcReturn } from 'birpc' import type { MockMap } from './mocker' import type { ResolvedConfig } from './config' -import type { File, TaskResultPack, Test } from './tasks' import type { SnapshotResult } from './snapshot' import type { UserConsoleLog } from './general' @@ -46,6 +46,5 @@ export interface WorkerGlobalState { current?: Test filepath?: string moduleCache: ModuleCacheMap - browserHashMap?: Map mockMap: MockMap } diff --git a/packages/vitest/src/utils/base.ts b/packages/vitest/src/utils/base.ts index dd4faa6838c3..1761ffd739cf 100644 --- a/packages/vitest/src/utils/base.ts +++ b/packages/vitest/src/utils/base.ts @@ -1,4 +1,3 @@ -import { RealDate } from '../integrations/mock/date' import type { Arrayable, DeepMerge, Nullable } from '../types' function isFinalObj(obj: any) { @@ -49,43 +48,6 @@ export function getType(value: unknown): string { return Object.prototype.toString.apply(value).slice(8, -1) } -function getOwnProperties(obj: any) { - const ownProps = new Set() - if (isFinalObj(obj)) - return [] - collectOwnProperties(obj, ownProps) - return Array.from(ownProps) -} - -export function deepClone(val: T): T { - const seen = new WeakMap() - return clone(val, seen) -} - -export function clone(val: T, seen: WeakMap): T { - let k: any, out: any - if (seen.has(val)) - return seen.get(val) - if (Array.isArray(val)) { - out = Array(k = val.length) - seen.set(val, out) - while (k--) - out[k] = clone(val[k], seen) - return out as any - } - - if (Object.prototype.toString.call(val) === '[object Object]') { - out = Object.create(Object.getPrototypeOf(val)) - seen.set(val, out) - // we don't need properties from prototype - const props = getOwnProperties(val) - for (const k of props) - out[k] = clone((val as any)[k], seen) - return out - } - - return val -} /** * Convert `Arrayable` to `Array` * @@ -147,35 +109,8 @@ function isMergeableObject(item: any): item is Object { return isPlainObject(item) && !Array.isArray(item) } -export function assertTypes(value: unknown, name: string, types: string[]): void { - const receivedType = typeof value - const pass = types.includes(receivedType) - if (!pass) - throw new TypeError(`${name} value must be ${types.join(' or ')}, received "${receivedType}"`) -} - export function stdout(): NodeJS.WriteStream { // @ts-expect-error Node.js maps process.stdout to console._stdout // eslint-disable-next-line no-console return console._stdout || process.stdout } - -function random(seed: number) { - const x = Math.sin(seed++) * 10000 - return x - Math.floor(x) -} - -export function shuffle(array: T[], seed = RealDate.now()): T[] { - let length = array.length - - while (length) { - const index = Math.floor(random(seed) * length--) - - const previous = array[length] - array[length] = array[index] - array[index] = previous - ++seed - } - - return array -} diff --git a/packages/vitest/src/utils/diff.ts b/packages/vitest/src/utils/diff.ts deleted file mode 100644 index 1b720554c974..000000000000 --- a/packages/vitest/src/utils/diff.ts +++ /dev/null @@ -1,128 +0,0 @@ -import c from 'picocolors' -import * as diff from 'diff' -import cliTruncate from 'cli-truncate' - -export function formatLine(line: string, outputTruncateLength?: number) { - return cliTruncate(line, (outputTruncateLength ?? (process.stdout?.columns || 80)) - 4) -} - -export interface DiffOptions { - noColor?: boolean - outputDiffMaxLines?: number - outputTruncateLength?: number - outputDiffLines?: number - showLegend?: boolean -} - -/** -* Returns unified diff between two strings with coloured ANSI output. -* -* @private -* @param {String} actual -* @param {String} expected -* @return {string} The diff. -*/ - -export function unifiedDiff(actual: string, expected: string, options: DiffOptions = {}) { - if (actual === expected) - return '' - - const { outputTruncateLength, outputDiffLines, outputDiffMaxLines, noColor, showLegend = true } = options - - const indent = ' ' - const diffLimit = outputDiffLines || 15 - const diffMaxLines = outputDiffMaxLines || 50 - - const counts = { - '+': 0, - '-': 0, - } - let previousState: '-' | '+' | null = null - let previousCount = 0 - - const str = (str: string) => str - const dim = noColor ? str : c.dim - const green = noColor ? str : c.green - const red = noColor ? str : c.red - function preprocess(line: string) { - if (!line || line.match(/\\ No newline/)) - return - - const char = line[0] as '+' | '-' - if ('-+'.includes(char)) { - if (previousState !== char) { - previousState = char - previousCount = 0 - } - previousCount++ - counts[char]++ - if (previousCount === diffLimit) - return dim(`${char} ...`) - else if (previousCount > diffLimit) - return - } - return line - } - - const msg = diff.createPatch('string', expected, actual) - let lines = msg.split('\n').slice(5).map(preprocess).filter(Boolean) as string[] - let moreLines = 0 - const isCompact = counts['+'] === 1 && counts['-'] === 1 && lines.length === 2 - - if (lines.length > diffMaxLines) { - const firstDiff = lines.findIndex(line => line[0] === '-' || line[0] === '+') - const displayLines = lines.slice(firstDiff - 2, diffMaxLines) - const lastDisplayedIndex = firstDiff - 2 + diffMaxLines - if (lastDisplayedIndex < lines.length) - moreLines = lines.length - lastDisplayedIndex - lines = displayLines - } - - let formatted = lines.map((line: string) => { - line = line.replace(/\\"/g, '"') - if (line[0] === '-') { - line = formatLine(line.slice(1), outputTruncateLength) - if (isCompact) - return green(line) - return green(`- ${formatLine(line, outputTruncateLength)}`) - } - if (line[0] === '+') { - line = formatLine(line.slice(1), outputTruncateLength) - if (isCompact) - return red(line) - return red(`+ ${formatLine(line, outputTruncateLength)}`) - } - if (line.match(/@@/)) - return '--' - return ` ${line}` - }) - - if (moreLines) - formatted.push(dim(`... ${moreLines} more lines`)) - - if (showLegend) { - // Compact mode - if (isCompact) { - formatted = [ - `${green('- Expected')} ${formatted[0]}`, - `${red('+ Received')} ${formatted[1]}`, - ] - } - else { - if (formatted[0].includes('"')) - formatted[0] = formatted[0].replace('"', '') - - const last = formatted.length - 1 - if (formatted[last].endsWith('"')) - formatted[last] = formatted[last].slice(0, formatted[last].length - 1) - - formatted.unshift( - green(`- Expected - ${counts['-']}`), - red(`+ Received + ${counts['+']}`), - '', - ) - } - } - - return formatted.map(i => i ? (indent + i) : i).join('\n') -} diff --git a/packages/vitest/src/utils/import.ts b/packages/vitest/src/utils/import.ts index 16dab1312edf..0cefc901d256 100644 --- a/packages/vitest/src/utils/import.ts +++ b/packages/vitest/src/utils/import.ts @@ -1,7 +1,8 @@ import { getWorkerState } from './global' -import { setTimeout } from './timers' +import { getSafeTimers } from './timers' function waitNextTick() { + const { setTimeout } = getSafeTimers() return new Promise(resolve => setTimeout(resolve, 0)) } diff --git a/packages/vitest/src/utils/index.ts b/packages/vitest/src/utils/index.ts index 73365d759e5a..728d6fee66ea 100644 --- a/packages/vitest/src/utils/index.ts +++ b/packages/vitest/src/utils/index.ts @@ -1,13 +1,7 @@ -import { relative as relativeBrowser } from 'node:path' -import c from 'picocolors' -import { isPackageExists } from 'local-pkg' -import { relative as relativeNode } from 'pathe' +import { relative } from 'pathe' import type { ModuleCacheMap } from 'vite-node' -import type { Suite, Task } from '../types' -import { EXIT_CODE_RESTART } from '../constants' import { getWorkerState } from '../utils' -import { getNames } from './tasks' -import { isBrowser, isCI, isNode } from './env' +import { isNode } from './env' export * from './graph' export * from './tasks' @@ -22,28 +16,8 @@ export const getRunMode = () => getWorkerState().config.mode export const isRunningInTest = () => getRunMode() === 'test' export const isRunningInBenchmark = () => getRunMode() === 'benchmark' -export const relativePath = isBrowser ? relativeBrowser : relativeNode - -/** - * Partition in tasks groups by consecutive concurrent - */ -export function partitionSuiteChildren(suite: Suite) { - let tasksGroup: Task[] = [] - const tasksGroups: Task[][] = [] - for (const c of suite.tasks) { - if (tasksGroup.length === 0 || c.concurrent === tasksGroup[0].concurrent) { - tasksGroup.push(c) - } - else { - tasksGroups.push(tasksGroup) - tasksGroup = [c] - } - } - if (tasksGroup.length > 0) - tasksGroups.push(tasksGroup) - - return tasksGroups -} +export const relativePath = relative +export { resolve } from 'pathe' export function resetModules(modules: ModuleCacheMap, resetMocks = false) { const skipPaths = [ @@ -64,10 +38,6 @@ export function resetModules(modules: ModuleCacheMap, resetMocks = false) { }) } -export function getFullName(task: Task) { - return getNames(task).join(c.dim(' > ')) -} - export function removeUndefinedValues>(obj: T): T { for (const key in Object.keys(obj)) { if (obj[key] === undefined) @@ -76,38 +46,6 @@ export function removeUndefinedValues>(obj: T): T return obj } -export async function ensurePackageInstalled( - dependency: string, - root: string, -) { - if (isPackageExists(dependency, { paths: [root] })) - return true - - const promptInstall = !isCI && process.stdout.isTTY - - process.stderr.write(c.red(`${c.inverse(c.red(' MISSING DEP '))} Can not find dependency '${dependency}'\n\n`)) - - if (!promptInstall) - return false - - const prompts = await import('prompts') - const { install } = await prompts.prompt({ - type: 'confirm', - name: 'install', - message: c.reset(`Do you want to install ${c.green(dependency)}?`), - }) - - if (install) { - await (await import('@antfu/install-pkg')).installPackage(dependency, { dev: true }) - // TODO: somehow it fails to load the package after installation, remove this when it's fixed - process.stderr.write(c.yellow(`\nPackage ${dependency} installed, re-run the command to start.\n`)) - process.exit(EXIT_CODE_RESTART) - return true - } - - return false -} - /** * If code starts with a function call, will return its last index, respecting arguments. * This will return 25 - last ending character of toMatch ")" @@ -150,10 +88,6 @@ export function getCallLastIndex(code: string) { return null } -const resolve = isNode ? relativeNode : relativeBrowser - -export { resolve as resolvePath } - // AggregateError is supported in Node.js 15.0.0+ class AggregateErrorPonyfill extends Error { errors: unknown[] @@ -164,25 +98,6 @@ class AggregateErrorPonyfill extends Error { } export { AggregateErrorPonyfill as AggregateError } -type DeferPromise = Promise & { - resolve: (value: T | PromiseLike) => void - reject: (reason?: any) => void -} - -export function createDefer(): DeferPromise { - let resolve: ((value: T | PromiseLike) => void) | null = null - let reject: ((reason?: any) => void) | null = null - - const p = new Promise((_resolve, _reject) => { - resolve = _resolve - reject = _reject - }) as DeferPromise - - p.resolve = resolve! - p.reject = reject! - return p -} - export function objectAttr(source: any, path: string, defaultValue = undefined) { // a[3].b -> a.3.b const paths = path.replace(/\[(\d+)\]/g, '.$1').split('.') diff --git a/packages/vitest/src/utils/source-map.ts b/packages/vitest/src/utils/source-map.ts index f071a1395b3f..da40409f7e4a 100644 --- a/packages/vitest/src/utils/source-map.ts +++ b/packages/vitest/src/utils/source-map.ts @@ -6,6 +6,8 @@ export const lineSplitRE = /\r?\n/ const stackIgnorePatterns = [ 'node:internal', + /\/packages\/\w+\/dist\//, + /\/@vitest\/\w+\/dist\//, '/vitest/dist/', '/vitest/src/', '/vite-node/dist/', @@ -86,7 +88,7 @@ export function parseStacktrace(e: ErrorWithDiff, full = false): ParsedStack[] { .map((raw): ParsedStack | null => { const stack = parseSingleStack(raw) - if (!stack || (!full && stackIgnorePatterns.some(p => stack.file.includes(p)))) + if (!stack || (!full && stackIgnorePatterns.some(p => stack.file.match(p)))) return null return stack diff --git a/packages/vitest/src/utils/tasks.ts b/packages/vitest/src/utils/tasks.ts index 70c547567b83..30146713ee7c 100644 --- a/packages/vitest/src/utils/tasks.ts +++ b/packages/vitest/src/utils/tasks.ts @@ -1,32 +1,11 @@ -import type { Arrayable, Benchmark, Suite, Task, Test } from '../types' +import { getNames, getTests } from '@vitest/runner/utils' +import type { Arrayable, Suite, Task } from '../types' import { toArray } from './base' -function isAtomTest(s: Task): s is Test | Benchmark { - return (s.type === 'test' || s.type === 'benchmark') -} - -export function getTests(suite: Arrayable): (Test | Benchmark)[] { - return toArray(suite).flatMap(s => isAtomTest(s) ? [s] : s.tasks.flatMap(c => isAtomTest(c) ? [c] : getTests(c))) -} - -export function getTasks(tasks: Arrayable = []): Task[] { - return toArray(tasks).flatMap(s => isAtomTest(s) ? [s] : [s, ...getTasks(s.tasks)]) -} - -export function getSuites(suite: Arrayable): Suite[] { - return toArray(suite).flatMap(s => s.type === 'suite' ? [s, ...getSuites(s.tasks)] : []) -} - -export function hasTests(suite: Arrayable): boolean { - return toArray(suite).some(s => s.tasks.some(c => isAtomTest(c) || hasTests(c))) -} +export { getTasks, getTests, getSuites, hasTests, hasFailed, getNames } from '@vitest/runner/utils' export function hasBenchmark(suite: Arrayable): boolean { - return toArray(suite).some(s => s?.tasks?.some(c => c.type === 'benchmark' || hasBenchmark(c as Suite))) -} - -export function hasFailed(suite: Arrayable): boolean { - return toArray(suite).some(s => s.result?.state === 'fail' || (s.type === 'suite' && hasFailed(s.tasks))) + return toArray(suite).some(s => s?.tasks?.some(c => c.meta?.benchmark || hasBenchmark(c as Suite))) } export function hasFailedSnapshot(suite: Arrayable): boolean { @@ -35,15 +14,6 @@ export function hasFailedSnapshot(suite: Arrayable): boolean { }) } -export function getNames(task: Task) { - const names = [task.name] - let current: Task | undefined = task - - while (current?.suite || current?.file) { - current = current.suite || current.file - if (current?.name) - names.unshift(current.name) - } - - return names +export function getFullName(task: Task, separator = ' > ') { + return getNames(task).join(separator) } diff --git a/packages/vitest/src/utils/timers.ts b/packages/vitest/src/utils/timers.ts index 55b0fd03579c..5e446ab0f285 100644 --- a/packages/vitest/src/utils/timers.ts +++ b/packages/vitest/src/utils/timers.ts @@ -1,13 +1,3 @@ -const { - setTimeout: safeSetTimeout, - setInterval: safeSetInterval, - clearInterval: safeClearInterval, - clearTimeout: safeClearTimeout, -} = globalThis - export { - safeSetTimeout as setTimeout, - safeSetInterval as setInterval, - safeClearInterval as clearInterval, - safeClearTimeout as clearTimeout, -} + getSafeTimers, +} from '@vitest/utils' diff --git a/packages/vitest/suite.d.ts b/packages/vitest/suite.d.ts new file mode 100644 index 000000000000..9465dc5c11d2 --- /dev/null +++ b/packages/vitest/suite.d.ts @@ -0,0 +1 @@ +export * from './dist/suite.js' diff --git a/packages/web-worker/rollup.config.js b/packages/web-worker/rollup.config.js index 92aee2ef6e24..8bd2ad296f88 100644 --- a/packages/web-worker/rollup.config.js +++ b/packages/web-worker/rollup.config.js @@ -44,7 +44,7 @@ export default () => [ plugins, }, { - input: entries, + input: 'src/pure.ts', output: { dir: process.cwd(), entryFileNames: '[name].d.ts', diff --git a/packages/ws-client/package.json b/packages/ws-client/package.json index c43a731bbb55..c15a699064e7 100644 --- a/packages/ws-client/package.json +++ b/packages/ws-client/package.json @@ -44,6 +44,7 @@ "ws": "^8.12.0" }, "devDependencies": { + "@vitest/runner": "workspace:*", "rollup": "^2.79.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c160b72af4b4..efacc9fcf190 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,7 +36,7 @@ importers: node-fetch-native: ^1.0.1 npm-run-all: ^4.1.5 ohmyfetch: ^0.4.21 - pathe: ^0.2.0 + pathe: ^1.1.0 pnpm: 7.23.0 rimraf: ^3.0.2 rollup: ^2.79.1 @@ -76,7 +76,7 @@ importers: node-fetch-native: 1.0.1 npm-run-all: 4.1.5 ohmyfetch: 0.4.21 - pathe: 0.2.0 + pathe: 1.1.0 pnpm: 7.23.0 rimraf: 3.0.2 rollup: 2.79.1 @@ -294,7 +294,7 @@ importers: '@types/react-test-renderer': 17.0.2 '@vitejs/plugin-react': 3.0.1_vite@4.0.0 '@vitest/ui': link:../../packages/ui - happy-dom: 8.1.4 + happy-dom: 8.1.5 jsdom: 21.0.0 react-test-renderer: 17.0.2_react@17.0.2 vite: 4.0.0 @@ -626,6 +626,7 @@ importers: packages/browser: specifiers: '@types/ws': ^8.5.4 + '@vitest/runner': workspace:* '@vitest/ws-client': workspace:* local-pkg: ^0.4.2 mlly: ^1.1.0 @@ -635,6 +636,7 @@ importers: sirv: ^2.0.2 vitest: workspace:* dependencies: + '@vitest/runner': link:../runner local-pkg: 0.4.2 mlly: 1.1.0 modern-node-polyfills: 0.0.9 @@ -649,14 +651,14 @@ importers: packages/coverage-c8: specifiers: c8: ^7.12.0 - pathe: ^0.2.0 + pathe: ^1.1.0 vite-node: workspace:* vitest: workspace:* dependencies: c8: 7.12.0 vitest: link:../vitest devDependencies: - pathe: 0.2.0 + pathe: 1.1.0 vite-node: link:../vite-node packages/coverage-istanbul: @@ -671,7 +673,7 @@ importers: istanbul-lib-report: ^3.0.0 istanbul-lib-source-maps: ^4.0.1 istanbul-reports: ^3.1.5 - pathe: ^0.2.0 + pathe: ^1.1.0 test-exclude: ^6.0.0 vitest: workspace:* dependencies: @@ -688,7 +690,7 @@ importers: '@types/istanbul-lib-report': 3.0.0 '@types/istanbul-lib-source-maps': 4.0.1 '@types/istanbul-reports': 3.0.1 - pathe: 0.2.0 + pathe: 1.1.0 packages/expect: specifiers: @@ -700,8 +702,19 @@ importers: '@vitest/spy': link:../spy '@vitest/utils': link:../utils chai: 4.3.7 + devDependencies: picocolors: 1.0.0 + packages/runner: + specifiers: + '@vitest/utils': workspace:* + p-limit: ^4.0.0 + pathe: ^1.1.0 + dependencies: + '@vitest/utils': link:../utils + p-limit: 4.0.0 + pathe: 1.1.0 + packages/spy: specifiers: tinyspy: ^1.0.2 @@ -719,6 +732,7 @@ importers: '@unocss/reset': ^0.48.3 '@vitejs/plugin-vue': ^4.0.0 '@vitejs/plugin-vue-jsx': ^3.0.0 + '@vitest/runner': workspace:* '@vitest/ws-client': workspace:* '@vueuse/core': ^9.10.0 ansi-to-html: ^0.7.2 @@ -731,6 +745,7 @@ importers: fast-glob: ^3.2.12 flatted: ^3.2.7 floating-vue: ^2.0.0-y.0 + pathe: ^1.1.0 picocolors: ^1.0.0 rollup: ^2.79.1 sirv: ^2.0.2 @@ -745,6 +760,8 @@ importers: dependencies: fast-glob: 3.2.12 flatted: 3.2.7 + pathe: 1.1.0 + picocolors: 1.0.0 sirv: 2.0.2 devDependencies: '@faker-js/faker': 7.6.0 @@ -756,6 +773,7 @@ importers: '@unocss/reset': 0.48.3 '@vitejs/plugin-vue': 4.0.0_vite@4.0.0+vue@3.2.45 '@vitejs/plugin-vue-jsx': 3.0.0_vite@4.0.0+vue@3.2.45 + '@vitest/runner': link:../runner '@vitest/ws-client': link:../ws-client '@vueuse/core': 9.10.0_vue@3.2.45 ansi-to-html: 0.7.2 @@ -766,7 +784,6 @@ importers: d3-graph-controller: 2.5.1 diff: 5.1.0 floating-vue: 2.0.0-y.0_vue@3.2.45 - picocolors: 1.0.0 rollup: 2.79.1 splitpanes: 3.1.5 unocss: 0.48.3_rollup@2.79.1+vite@4.0.0 @@ -782,11 +799,13 @@ importers: '@types/diff': ^5.0.2 cli-truncate: ^3.1.0 diff: ^5.1.0 + loupe: ^2.3.6 picocolors: ^1.0.0 pretty-format: ^27.5.1 dependencies: cli-truncate: 3.1.0 diff: 5.1.0 + loupe: 2.3.6 picocolors: 1.0.0 pretty-format: 27.5.1 devDependencies: @@ -800,7 +819,7 @@ importers: cac: ^6.7.14 debug: ^4.3.4 mlly: ^1.1.0 - pathe: ^0.2.0 + pathe: ^1.1.0 picocolors: ^1.0.0 rollup: ^2.79.1 source-map: ^0.6.1 @@ -810,7 +829,7 @@ importers: cac: 6.7.14 debug: 4.3.4 mlly: 1.1.0 - pathe: 0.2.0 + pathe: 1.1.0 picocolors: 1.0.0 source-map: 0.6.1 source-map-support: 0.5.21 @@ -836,6 +855,7 @@ importers: '@types/prompts': ^2.4.2 '@types/sinonjs__fake-timers': ^8.1.2 '@vitest/expect': workspace:* + '@vitest/runner': workspace:* '@vitest/spy': workspace:* '@vitest/ui': workspace:* '@vitest/utils': workspace:* @@ -864,7 +884,7 @@ importers: mlly: ^1.1.0 natural-compare: ^1.4.0 p-limit: ^4.0.0 - pathe: ^0.2.0 + pathe: ^1.1.0 picocolors: ^1.0.0 pkg-types: ^1.0.1 pretty-format: ^27.5.1 @@ -886,12 +906,18 @@ importers: '@types/chai': 4.3.4 '@types/chai-subset': 1.3.3 '@types/node': 18.7.13 + '@vitest/expect': link:../expect + '@vitest/runner': link:../runner + '@vitest/spy': link:../spy + '@vitest/ui': link:../ui + '@vitest/utils': link:../utils acorn: 8.8.1 acorn-walk: 8.2.0 cac: 6.7.14 chai: 4.3.7 debug: 4.3.4 local-pkg: 0.4.2 + pathe: 1.1.0 picocolors: 1.0.0 source-map: 0.6.1 std-env: 3.3.1 @@ -912,10 +938,6 @@ importers: '@types/natural-compare': 1.4.1 '@types/prompts': 2.4.2 '@types/sinonjs__fake-timers': 8.1.2 - '@vitest/expect': link:../expect - '@vitest/spy': link:../spy - '@vitest/ui': link:../ui - '@vitest/utils': link:../utils birpc: 0.2.3 chai-subset: 1.6.0 cli-truncate: 3.1.0 @@ -935,7 +957,6 @@ importers: mlly: 1.1.0 natural-compare: 1.4.0 p-limit: 4.0.0 - pathe: 0.2.0 pkg-types: 1.0.1 pretty-format: 27.5.1 prompts: 2.4.2 @@ -963,6 +984,7 @@ importers: packages/ws-client: specifiers: + '@vitest/runner': workspace:* birpc: ^0.2.3 flatted: ^3.2.7 rollup: ^2.79.1 @@ -972,6 +994,7 @@ importers: flatted: 3.2.7 ws: 8.12.0 devDependencies: + '@vitest/runner': link:../runner rollup: 2.79.1 test/base: @@ -1024,10 +1047,16 @@ importers: test/core: specifiers: + '@vitest/expect': workspace:^0.27.2 + '@vitest/runner': workspace:* + '@vitest/utils': workspace:* tinyspy: ^1.0.2 url: ^0.11.0 vitest: workspace:* devDependencies: + '@vitest/expect': link:../../packages/expect + '@vitest/runner': link:../../packages/runner + '@vitest/utils': link:../../packages/utils tinyspy: 1.0.2 url: 0.11.0 vitest: link:../../packages/vitest @@ -1043,7 +1072,7 @@ importers: devDependencies: '@vitejs/plugin-vue': 4.0.0_vite@4.0.0+vue@3.2.45 '@vue/test-utils': 2.2.7_vue@3.2.45 - happy-dom: 8.1.4 + happy-dom: 8.1.5 vite: 4.0.0 vitest: link:../../packages/vitest vue: 3.2.45 @@ -1221,10 +1250,10 @@ importers: test/vite-config: specifiers: - pathe: ^0.2.0 + pathe: ^1.1.0 vitest: workspace:* devDependencies: - pathe: 0.2.0 + pathe: 1.1.0 vitest: link:../../packages/vitest test/vite-node: @@ -7948,7 +7977,7 @@ packages: consola: 2.15.3 fast-glob: 3.2.12 magic-string: 0.27.0 - pathe: 1.0.0 + pathe: 1.1.0 perfect-debounce: 0.1.3 transitivePeerDependencies: - rollup @@ -7970,7 +7999,7 @@ packages: consola: 2.15.3 fast-glob: 3.2.12 magic-string: 0.27.0 - pathe: 1.0.0 + pathe: 1.1.0 perfect-debounce: 0.1.3 transitivePeerDependencies: - rollup @@ -13571,8 +13600,8 @@ packages: - encoding dev: true - /happy-dom/8.1.4: - resolution: {integrity: sha512-mUCzXHhSO6fOQlZwKW6z2f/+rYavKNxNrgY4nJ4dp+r8gTGbTENgMZGfM6eJD0DJPRFF8DFyngXdBF93wF96UA==} + /happy-dom/8.1.5: + resolution: {integrity: sha512-/UXAJ2fHTs4H3vy7TS7c9PKFvPyaNialk2Er9NdXfpBKNaCITMOH03rkjHXp5jnJnSmRBa+av8E08PUAaIB1jQ==} dependencies: css.escape: 1.5.1 he: 1.2.0 @@ -15456,6 +15485,12 @@ packages: get-func-name: 2.0.0 dev: false + /loupe/2.3.6: + resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + dependencies: + get-func-name: 2.0.0 + dev: false + /lower-case/2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: @@ -16666,7 +16701,6 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: yocto-queue: 1.0.0 - dev: true /p-locate/3.0.0: resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} @@ -16927,9 +16961,6 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - /pathe/0.2.0: - resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} - /pathe/0.3.9: resolution: {integrity: sha512-6Y6s0vT112P3jD8dGfuS6r+lpa0qqNrLyHPOwvXMnyNTQaYiwgau2DP3aNDsR13xqtGj7rrPo+jFUATpU6/s+g==} dev: true @@ -16937,6 +16968,9 @@ packages: /pathe/1.0.0: resolution: {integrity: sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==} + /pathe/1.1.0: + resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} + /pathval/1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: false @@ -17090,7 +17124,7 @@ packages: dependencies: jsonc-parser: 3.2.0 mlly: 1.1.0 - pathe: 1.0.0 + pathe: 1.1.0 /playwright-core/1.28.0: resolution: {integrity: sha512-nJLknd28kPBiCNTbqpu6Wmkrh63OEqJSFw9xOfL9qxfNwody7h6/L3O2dZoWQ6Oxcm0VOHjWmGiCUGkc0X3VZA==} @@ -20236,7 +20270,7 @@ packages: local-pkg: 0.4.2 magic-string: 0.27.0 mlly: 1.1.0 - pathe: 1.0.0 + pathe: 1.1.0 pkg-types: 1.0.1 scule: 1.0.0 strip-literal: 1.0.0 @@ -21808,7 +21842,6 @@ packages: /yocto-queue/1.0.0: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} - dev: true /zen-observable-ts/1.2.5: resolution: {integrity: sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==} diff --git a/test/browser/test/__snapshots__/snapshot.test.ts.snap b/test/browser/test/__snapshots__/snapshot.test.ts.snap new file mode 100644 index 000000000000..5f969b0acbdc --- /dev/null +++ b/test/browser/test/__snapshots__/snapshot.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1 + +exports[`file snapshot 1`] = `1`; diff --git a/test/browser/test/snapshot.test.ts b/test/browser/test/snapshot.test.ts new file mode 100644 index 000000000000..ed80a694376c --- /dev/null +++ b/test/browser/test/snapshot.test.ts @@ -0,0 +1,9 @@ +import { expect, test } from 'vitest' + +test('inline snapshot', () => { + expect(1).toMatchInlineSnapshot('1') +}) + +test('file snapshot', () => { + expect(1).toMatchSnapshot() +}) diff --git a/test/core/package.json b/test/core/package.json index 58f30d382ef8..7d207920e065 100644 --- a/test/core/package.json +++ b/test/core/package.json @@ -6,6 +6,9 @@ "coverage": "vitest run --coverage" }, "devDependencies": { + "@vitest/expect": "workspace:^0.27.2", + "@vitest/runner": "workspace:*", + "@vitest/utils": "workspace:*", "tinyspy": "^1.0.2", "url": "^0.11.0", "vitest": "workspace:*" diff --git a/test/core/test/__snapshots__/mocked.test.ts.snap b/test/core/test/__snapshots__/mocked.test.ts.snap index 858f49ee0236..e896ff91c76d 100644 --- a/test/core/test/__snapshots__/mocked.test.ts.snap +++ b/test/core/test/__snapshots__/mocked.test.ts.snap @@ -6,8 +6,8 @@ exports[`mocked function which fails on toReturnWith > just one call 1`] = ` Received:  1st spy call return: - 2 - 1 + 1 + 2  Number of calls: 1 @@ -20,18 +20,18 @@ exports[`mocked function which fails on toReturnWith > multi calls 1`] = ` Received:  1st spy call return: - 2 - 1 + 1 + 2  2nd spy call return: - 2 - 1 + 1 + 2  3rd spy call return: - 2 - 1 + 1 + 2  Number of calls: 3 @@ -45,22 +45,22 @@ Received:  1st spy call return:  Object { - - \\"a\\": \\"4\\", - + \\"a\\": \\"1\\", + - \\"a\\": \\"1\\", + + \\"a\\": \\"4\\", }  2nd spy call return:  Object { - - \\"a\\": \\"4\\", - + \\"a\\": \\"1\\", + - \\"a\\": \\"1\\", + + \\"a\\": \\"4\\", }  3rd spy call return:  Object { - - \\"a\\": \\"4\\", - + \\"a\\": \\"1\\", + - \\"a\\": \\"1\\", + + \\"a\\": \\"4\\", }  diff --git a/test/core/test/chainable.test.ts b/test/core/test/chainable.test.ts index ac7148f7b18e..d5f880f7c308 100644 --- a/test/core/test/chainable.test.ts +++ b/test/core/test/chainable.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createChainable } from '../../../packages/vitest/src/runtime/chain' +import { createChainable } from '@vitest/runner/utils' describe('chainable', () => { it('creates', () => { diff --git a/test/core/test/diff.test.ts b/test/core/test/diff.test.ts index d57fcd938e61..04f8e0df4cff 100644 --- a/test/core/test/diff.test.ts +++ b/test/core/test/diff.test.ts @@ -6,7 +6,7 @@ test('displays an error for large objects', () => { const objectA = new Array(1000).fill(0).map((_, i) => ({ i, long: 'a'.repeat(i) })) const objectB = new Array(1000).fill(0).map((_, i) => ({ i, long: 'b'.repeat(i) })) const console = { log: vi.fn(), error: vi.fn() } - displayDiff(stringify(objectA), stringify(objectB), console as any, { noColor: true }) + displayDiff(stringify(objectA), stringify(objectB), console as any) expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(` "Could not display diff. It's possible objects are too large to compare. Try increasing --outputDiffMaxSize option. @@ -16,7 +16,7 @@ test('displays an error for large objects', () => { test('displays an error for large objects', () => { const console = { log: vi.fn(), error: vi.fn() } - displayDiff(stringify('undefined'), stringify('undefined'), console as any, { noColor: true }) + displayDiff(stringify('undefined'), stringify('undefined'), console as any) expect(console.error).not.toHaveBeenCalled() }) @@ -24,7 +24,7 @@ test('displays diff', () => { const objectA = { a: 1, b: 2 } const objectB = { a: 1, b: 3 } const console = { log: vi.fn(), error: vi.fn() } - displayDiff(stringify(objectA), stringify(objectB), console as any, { noColor: true }) + displayDiff(stringify(objectA), stringify(objectB), console as any) expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(` " - Expected - 1 + Received + 1 @@ -42,7 +42,7 @@ test('displays long diff', () => { const objectA = { a: 1, b: 2, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15, p: 16, q: 17, r: 18, s: 19, t: 20, u: 21, v: 22, w: 23, x: 24, y: 25, z: 26 } const objectB = { a: 1, b: 3, k: 11, l: 12, m: 13, n: 14, p: 16, o: 17, r: 18, s: 23, t: 88, u: 21, v: 44, w: 23, x: 24, y: 25, z: 26 } const console = { log: vi.fn(), error: vi.fn() } - displayDiff(stringify(objectA), stringify(objectB), console as any, { noColor: true, outputDiffMaxLines: 5 }) + displayDiff(stringify(objectA), stringify(objectB), console as any, { outputDiffMaxLines: 5 }) expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(` " - Expected - 5 + Received + 13 @@ -65,7 +65,7 @@ Morbi consectetur arcu nec lorem lacinia tempus.` Quisque pellentesque enim a elit faucibus cursus. Sed in tellus aliquet mauris interdum semper a in lacus.` const console = { log: vi.fn(), error: vi.fn() } - displayDiff((stringA), (objectB), console as any, { noColor: true, outputTruncateLength: 14 }) + displayDiff((stringA), (objectB), console as any, { outputTruncateLength: 14 }) expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(` " - Expected - 3 + Received + 3 diff --git a/test/core/test/replace-matcher.test.ts b/test/core/test/replace-matcher.test.ts index c4aa53e9a94e..d6e21c4f6c52 100644 --- a/test/core/test/replace-matcher.test.ts +++ b/test/core/test/replace-matcher.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { replaceAsymmetricMatcher } from '../../../packages/vitest/src/runtime/error' +import { replaceAsymmetricMatcher } from '@vitest/runner/utils' describe('replace asymmetric matcher', () => { const expectReplaceAsymmetricMatcher = (actual: any, expected: any) => { diff --git a/test/core/test/serialize.test.ts b/test/core/test/serialize.test.ts index 6179c8cddb8d..325d9add9a6f 100644 --- a/test/core/test/serialize.test.ts +++ b/test/core/test/serialize.test.ts @@ -1,7 +1,7 @@ // @vitest-environment jsdom import { describe, expect, it } from 'vitest' -import { serializeError } from '../../../packages/vitest/src/runtime/error' +import { serializeError } from '@vitest/runner/utils' describe('error serialize', () => { it('works', () => { diff --git a/test/core/test/utils.spec.ts b/test/core/test/utils.spec.ts index 7ebfda54a73d..3f9fa65eb49c 100644 --- a/test/core/test/utils.spec.ts +++ b/test/core/test/utils.spec.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from 'vitest' -import { assertTypes, deepClone, deepMerge, objectAttr, resetModules, toArray } from '../../../packages/vitest/src/utils' +import { assertTypes, deepClone, objectAttr, toArray } from '@vitest/utils' +import { deepMerge, resetModules } from '../../../packages/vitest/src/utils' import { deepMergeSnapshot } from '../../../packages/vitest/src/integrations/snapshot/port/utils' import type { ModuleCacheMap } from '../../../packages/vite-node/src/types' diff --git a/test/core/vitest.config.ts b/test/core/vitest.config.ts index fa07e6ba8474..4a675e9e7b84 100644 --- a/test/core/vitest.config.ts +++ b/test/core/vitest.config.ts @@ -35,7 +35,6 @@ export default defineConfig({ alias: [ { find: '#', replacement: resolve(__dirname, 'src') }, { find: '$', replacement: 'src' }, - { find: '@vitest', replacement: resolve(__dirname, '..', '..', 'packages') }, ], }, test: { diff --git a/test/reporters/src/context.ts b/test/reporters/src/context.ts index 38c67c54067d..69276ca7e2e8 100644 --- a/test/reporters/src/context.ts +++ b/test/reporters/src/context.ts @@ -13,7 +13,7 @@ export function getContext(): Context { let output = '' const config: Partial = { - root: '/', + root: '/vitest', } const moduleGraph: Partial = { diff --git a/test/reporters/tests/__snapshots__/html.test.ts.snap b/test/reporters/tests/__snapshots__/html.test.ts.snap index e5772658ef16..49db8c36242b 100644 --- a/test/reporters/tests/__snapshots__/html.test.ts.snap +++ b/test/reporters/tests/__snapshots__/html.test.ts.snap @@ -103,7 +103,9 @@ exports[`html reporter > resolves to "failing" status for test file "json-fail" ], }, }, - "paths": [], + "paths": [ + "/test/reporters/fixtures/json-fail.test.ts", + ], } `; @@ -213,6 +215,8 @@ exports[`html reporter > resolves to "passing" status for test file "all-passing ], }, }, - "paths": [], + "paths": [ + "/test/reporters/fixtures/all-passing-or-skipped.test.ts", + ], } `; diff --git a/test/reporters/tests/__snapshots__/reporters.spec.ts.snap b/test/reporters/tests/__snapshots__/reporters.spec.ts.snap index e3ba742d5659..6ec2907cdcb3 100644 --- a/test/reporters/tests/__snapshots__/reporters.spec.ts.snap +++ b/test/reporters/tests/__snapshots__/reporters.spec.ts.snap @@ -7,7 +7,7 @@ exports[`JUnit reporter (no outputFile entry) 1`] = ` AssertionError: expected 2.23606797749979 to equal 2 - ❯ vitest/test/core/test/basic.test.ts:8:32 + ❯ test/core/test/basic.test.ts:8:32 @@ -43,7 +43,7 @@ exports[`JUnit reporter 1`] = ` AssertionError: expected 2.23606797749979 to equal 2 - ❯ vitest/test/core/test/basic.test.ts:8:32 + ❯ test/core/test/basic.test.ts:8:32 @@ -84,7 +84,7 @@ exports[`JUnit reporter with outputFile 2`] = ` AssertionError: expected 2.23606797749979 to equal 2 - ❯ vitest/test/core/test/basic.test.ts:8:32 + ❯ test/core/test/basic.test.ts:8:32 @@ -125,7 +125,7 @@ exports[`JUnit reporter with outputFile in non-existing directory 2`] = ` AssertionError: expected 2.23606797749979 to equal 2 - ❯ vitest/test/core/test/basic.test.ts:8:32 + ❯ test/core/test/basic.test.ts:8:32 @@ -166,7 +166,7 @@ exports[`JUnit reporter with outputFile object 2`] = ` AssertionError: expected 2.23606797749979 to equal 2 - ❯ vitest/test/core/test/basic.test.ts:8:32 + ❯ test/core/test/basic.test.ts:8:32 @@ -207,7 +207,7 @@ exports[`JUnit reporter with outputFile object in non-existing directory 2`] = ` AssertionError: expected 2.23606797749979 to equal 2 - ❯ vitest/test/core/test/basic.test.ts:8:32 + ❯ test/core/test/basic.test.ts:8:32 @@ -248,7 +248,7 @@ exports[`JUnit reporter with outputFile with XML in error message 2`] = ` AssertionError: error message that has XML in it <tag> - ❯ vitest/test/core/test/basic.test.ts:8:32 + ❯ test/core/test/basic.test.ts:8:32 diff --git a/test/snapshots/tools/generate-inline-test.mjs b/test/snapshots/tools/generate-inline-test.mjs index 133b448ca253..e72c75dccc63 100644 --- a/test/snapshots/tools/generate-inline-test.mjs +++ b/test/snapshots/tools/generate-inline-test.mjs @@ -1,8 +1,8 @@ import fs from 'fs/promises' import { fileURLToPath } from 'url' -import pathe from 'pathe' +import { dirname, resolve } from 'pathe' -const dirname = pathe.dirname(fileURLToPath(import.meta.url)) +const dir = dirname(fileURLToPath(import.meta.url)) export async function generateInlineTest(templatePath, testpath) { const template = await fs.readFile(templatePath, 'utf8') @@ -10,8 +10,8 @@ export async function generateInlineTest(templatePath, testpath) { console.log(`Generated ${testpath}`) } -const filepath = pathe.resolve(dirname, '../test-update/snapshots-inline-js.test.js') -const template = pathe.resolve(dirname, './inline-test-template.js'); +const filepath = resolve(dir, '../test-update/snapshots-inline-js.test.js') +const template = resolve(dir, './inline-test-template.js'); (async () => { await generateInlineTest(template, filepath) diff --git a/test/vite-config/package.json b/test/vite-config/package.json index f635dace48a5..d5372ea6f1f7 100644 --- a/test/vite-config/package.json +++ b/test/vite-config/package.json @@ -6,7 +6,7 @@ "coverage": "vitest run --coverage" }, "devDependencies": { - "pathe": "^0.2.0", + "pathe": "^1.1.0", "vitest": "workspace:*" } } diff --git a/tsconfig.json b/tsconfig.json index ebe03fc547a8..ec9831421c10 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,8 +17,11 @@ "@vitest/ws-client": ["./packages/ws-client/src/index.ts"], "@vitest/ui": ["./packages/ui/node/index.ts"], "@vitest/utils": ["./packages/utils/src/index.ts"], + "@vitest/utils/*": ["./packages/utils/src/*"], "@vitest/spy": ["./packages/spy/src/index.ts"], "@vitest/expect": ["./packages/expect/src/index.ts"], + "@vitest/runner": ["./packages/runner/src/index.ts"], + "@vitest/runner/*": ["./packages/runner/src/*"], "@vitest/browser": ["./packages/browser/src/node/index.ts"], "#types": ["./packages/vitest/src/index.ts"], "~/*": ["./packages/ui/client/*"],