From 7c036fdc07aa1679e4f65900a134856c3b65a1af Mon Sep 17 00:00:00 2001 From: Vladimir Date: Mon, 6 May 2024 10:55:37 +0200 Subject: [PATCH] fix: print unhandled errors to tets results tab (#370) * fix: print unhandled errors to tets results tab * chore: cleanup * fix: stop debugging if running other profiles * chore: update types/node and typecheck * fix: correctly watch folders, and add more debug statements * chore: files relative * chore: cleanup --- package.json | 4 +- pnpm-lock.yaml | 128 ++++++++++++++++-------------- src/api/rpc.ts | 2 +- src/debug/debugManager.ts | 4 + src/extension.ts | 11 ++- src/polyfills.ts | 11 +++ src/runner/runner.ts | 162 +++++++++++++++++++------------------- src/testTree.ts | 6 +- src/worker/reporter.ts | 26 +++++- src/worker/setupFile.ts | 12 ++- src/worker/watcher.ts | 36 ++++++++- 11 files changed, 247 insertions(+), 155 deletions(-) create mode 100644 src/polyfills.ts diff --git a/package.json b/package.json index 6d3bb12f..d1d8dea6 100644 --- a/package.json +++ b/package.json @@ -181,7 +181,7 @@ "@types/glob": "^7.2.0", "@types/micromatch": "^4.0.6", "@types/mocha": "^10.0.6", - "@types/node": "^18.11.18", + "@types/node": "^20.12.8", "@types/prompts": "^2.4.9", "@types/semver": "^7.3.9", "@types/vscode": "^1.77.0", @@ -216,7 +216,7 @@ "tree-kill": "^1.2.2", "tsup": "^8.0.1", "tsx": "^4.7.1", - "typescript": "^5.3.3", + "typescript": "^5.4.5", "vitest": "^1.4.0", "which": "^4.0.0", "ws": "^8.16.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0f90a3ae..612d653b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,7 +7,7 @@ settings: devDependencies: '@antfu/eslint-config': specifier: ^2.6.4 - version: 2.6.4(@vue/compiler-sfc@3.4.19)(eslint@8.56.0)(typescript@5.3.3)(vitest@1.4.0) + version: 2.6.4(@vue/compiler-sfc@3.4.19)(eslint@8.56.0)(typescript@5.4.5)(vitest@1.4.0) '@babel/parser': specifier: ^7.20.15 version: 7.23.9 @@ -33,8 +33,8 @@ devDependencies: specifier: ^10.0.6 version: 10.0.6 '@types/node': - specifier: ^18.11.18 - version: 18.19.17 + specifier: ^20.12.8 + version: 20.12.8 '@types/prompts': specifier: ^2.4.9 version: 2.4.9 @@ -133,16 +133,16 @@ devDependencies: version: 1.2.2 tsup: specifier: ^8.0.1 - version: 8.0.2(typescript@5.3.3) + version: 8.0.2(typescript@5.4.5) tsx: specifier: ^4.7.1 version: 4.7.1 typescript: - specifier: ^5.3.3 - version: 5.3.3 + specifier: ^5.4.5 + version: 5.4.5 vitest: specifier: ^1.4.0 - version: 1.4.0(@types/node@18.19.17) + version: 1.4.0(@types/node@20.12.8) which: specifier: ^4.0.0 version: 4.0.0 @@ -157,7 +157,7 @@ packages: engines: {node: '>=0.10.0'} dev: true - /@antfu/eslint-config@2.6.4(@vue/compiler-sfc@3.4.19)(eslint@8.56.0)(typescript@5.3.3)(vitest@1.4.0): + /@antfu/eslint-config@2.6.4(@vue/compiler-sfc@3.4.19)(eslint@8.56.0)(typescript@5.4.5)(vitest@1.4.0): resolution: {integrity: sha512-dMD/QC5KWS1OltdpKLhfZM7W7y7zils85opk8d4lyNr7yn0OFjZs7eMYtcC6DrrN2kQ1JrFvBM7uB0QdWn5PUQ==} hasBin: true peerDependencies: @@ -190,9 +190,9 @@ packages: '@eslint-types/jsdoc': 46.8.2-1 '@eslint-types/typescript-eslint': 6.21.0 '@eslint-types/unicorn': 50.0.1 - '@stylistic/eslint-plugin': 1.6.2(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@stylistic/eslint-plugin': 1.6.2(eslint@8.56.0)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.4.5) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.4.5) eslint: 8.56.0 eslint-config-flat-gitignore: 0.1.3 eslint-merge-processors: 0.1.0(eslint@8.56.0) @@ -204,11 +204,11 @@ packages: eslint-plugin-markdown: 3.0.1(eslint@8.56.0) eslint-plugin-n: 16.6.2(eslint@8.56.0) eslint-plugin-no-only-tests: 3.1.0 - eslint-plugin-perfectionist: 2.5.0(eslint@8.56.0)(typescript@5.3.3)(vue-eslint-parser@9.4.2) + eslint-plugin-perfectionist: 2.5.0(eslint@8.56.0)(typescript@5.4.5)(vue-eslint-parser@9.4.2) eslint-plugin-toml: 0.9.2(eslint@8.56.0) eslint-plugin-unicorn: 50.0.1(eslint@8.56.0) eslint-plugin-unused-imports: 3.1.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0) - eslint-plugin-vitest: 0.3.22(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0)(typescript@5.3.3)(vitest@1.4.0) + eslint-plugin-vitest: 0.3.22(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0)(typescript@5.4.5)(vitest@1.4.0) eslint-plugin-vue: 9.21.1(eslint@8.56.0) eslint-plugin-yml: 1.12.2(eslint@8.56.0) eslint-processor-vue-blocks: 0.1.1(@vue/compiler-sfc@3.4.19)(eslint@8.56.0) @@ -822,20 +822,20 @@ packages: picomatch: 4.0.1 dev: true - /@stylistic/eslint-plugin-plus@1.6.2(eslint@8.56.0)(typescript@5.3.3): + /@stylistic/eslint-plugin-plus@1.6.2(eslint@8.56.0)(typescript@5.4.5): resolution: {integrity: sha512-EDMwa6gzKw4bXRqdIAUvZDfIgwotbjJs8o+vYE22chAYtVAnA0Pcq+cPx0Uk35t2gvJWb5OaLDjqA6oy1tD0jg==} peerDependencies: eslint: '*' dependencies: '@types/eslint': 8.56.2 - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.4.5) eslint: 8.56.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@stylistic/eslint-plugin-ts@1.6.2(eslint@8.56.0)(typescript@5.3.3): + /@stylistic/eslint-plugin-ts@1.6.2(eslint@8.56.0)(typescript@5.4.5): resolution: {integrity: sha512-FizV58em0OjO/xFHRIy/LJJVqzxCNmYC/xVtKDf8aGDRgZpLo+lkaBKfBrbMkAGzhBKbYj+iLEFI4WEl6aVZGQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -843,14 +843,14 @@ packages: dependencies: '@stylistic/eslint-plugin-js': 1.6.2(eslint@8.56.0) '@types/eslint': 8.56.2 - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.4.5) eslint: 8.56.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@stylistic/eslint-plugin@1.6.2(eslint@8.56.0)(typescript@5.3.3): + /@stylistic/eslint-plugin@1.6.2(eslint@8.56.0)(typescript@5.4.5): resolution: {integrity: sha512-EFnVcKOE5HTiMlVwisL9hHjz8a69yBbJRscWF/z+/vl6M4ew8NVrBlY8ea7KdV8QtyCY4Yapmsbg5ZDfhWlEgg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -858,8 +858,8 @@ packages: dependencies: '@stylistic/eslint-plugin-js': 1.6.2(eslint@8.56.0) '@stylistic/eslint-plugin-jsx': 1.6.2(eslint@8.56.0) - '@stylistic/eslint-plugin-plus': 1.6.2(eslint@8.56.0)(typescript@5.3.3) - '@stylistic/eslint-plugin-ts': 1.6.2(eslint@8.56.0)(typescript@5.3.3) + '@stylistic/eslint-plugin-plus': 1.6.2(eslint@8.56.0)(typescript@5.4.5) + '@stylistic/eslint-plugin-ts': 1.6.2(eslint@8.56.0)(typescript@5.4.5) '@types/eslint': 8.56.2 eslint: 8.56.0 transitivePeerDependencies: @@ -938,6 +938,12 @@ packages: undici-types: 5.26.5 dev: true + /@types/node@20.12.8: + resolution: {integrity: sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==} + dependencies: + undici-types: 5.26.5 + dev: true + /@types/normalize-package-data@2.4.4: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} dev: true @@ -971,7 +977,7 @@ packages: '@types/node': 18.19.17 dev: true - /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3): + /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.4.5): resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -983,10 +989,10 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.4.5) '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/type-utils': 6.21.0(eslint@8.56.0)(typescript@5.4.5) + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.4.5) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.3.4(supports-color@8.1.1) eslint: 8.56.0 @@ -994,13 +1000,13 @@ packages: ignore: 5.3.1 natural-compare: 1.4.0 semver: 7.6.0 - ts-api-utils: 1.2.1(typescript@5.3.3) - typescript: 5.3.3 + ts-api-utils: 1.2.1(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3): + /@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.4.5): resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -1012,11 +1018,11 @@ packages: dependencies: '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.3.4(supports-color@8.1.1) eslint: 8.56.0 - typescript: 5.3.3 + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true @@ -1029,7 +1035,7 @@ packages: '@typescript-eslint/visitor-keys': 6.21.0 dev: true - /@typescript-eslint/type-utils@6.21.0(eslint@8.56.0)(typescript@5.3.3): + /@typescript-eslint/type-utils@6.21.0(eslint@8.56.0)(typescript@5.4.5): resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -1039,12 +1045,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.4.5) debug: 4.3.4(supports-color@8.1.1) eslint: 8.56.0 - ts-api-utils: 1.2.1(typescript@5.3.3) - typescript: 5.3.3 + ts-api-utils: 1.2.1(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true @@ -1054,7 +1060,7 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.21.0(typescript@5.3.3): + /@typescript-eslint/typescript-estree@6.21.0(typescript@5.4.5): resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -1070,13 +1076,13 @@ packages: is-glob: 4.0.3 minimatch: 9.0.3 semver: 7.6.0 - ts-api-utils: 1.2.1(typescript@5.3.3) - typescript: 5.3.3 + ts-api-utils: 1.2.1(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.21.0(eslint@8.56.0)(typescript@5.3.3): + /@typescript-eslint/utils@6.21.0(eslint@8.56.0)(typescript@5.4.5): resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -1087,7 +1093,7 @@ packages: '@types/semver': 7.5.7 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.5) eslint: 8.56.0 semver: 7.6.0 transitivePeerDependencies: @@ -2020,7 +2026,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.4.5) debug: 3.2.7 eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 @@ -2154,7 +2160,7 @@ packages: engines: {node: '>=5.0.0'} dev: true - /eslint-plugin-perfectionist@2.5.0(eslint@8.56.0)(typescript@5.3.3)(vue-eslint-parser@9.4.2): + /eslint-plugin-perfectionist@2.5.0(eslint@8.56.0)(typescript@5.4.5)(vue-eslint-parser@9.4.2): resolution: {integrity: sha512-F6XXcq4mKKUe/SREoMGQqzgw6cgCgf3pFzkFfQVIGtqD1yXVpQjnhTepzhBeZfxZwgMzR9HO4yH4CUhIQ2WBcQ==} peerDependencies: astro-eslint-parser: ^0.16.0 @@ -2172,7 +2178,7 @@ packages: vue-eslint-parser: optional: true dependencies: - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.4.5) eslint: 8.56.0 minimatch: 9.0.3 natural-compare-lite: 1.4.0 @@ -2234,12 +2240,12 @@ packages: '@typescript-eslint/eslint-plugin': optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.4.5) eslint: 8.56.0 eslint-rule-composer: 0.3.0 dev: true - /eslint-plugin-vitest@0.3.22(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0)(typescript@5.3.3)(vitest@1.4.0): + /eslint-plugin-vitest@0.3.22(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0)(typescript@5.4.5)(vitest@1.4.0): resolution: {integrity: sha512-atkFGQ7aVgcuSeSMDqnyevIyUpfBPMnosksgEPrKE7Y8xQlqG/5z2IQ6UDau05zXaaFv7Iz8uzqvIuKshjZ0Zw==} engines: {node: ^18.0.0 || >= 20.0.0} peerDependencies: @@ -2252,10 +2258,10 @@ packages: vitest: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.4.5) + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.4.5) eslint: 8.56.0 - vitest: 1.4.0(@types/node@18.19.17) + vitest: 1.4.0(@types/node@20.12.8) transitivePeerDependencies: - supports-color - typescript @@ -4245,13 +4251,13 @@ packages: hasBin: true dev: true - /ts-api-utils@1.2.1(typescript@5.3.3): + /ts-api-utils@1.2.1(typescript@5.4.5): resolution: {integrity: sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.3.3 + typescript: 5.4.5 dev: true /ts-interface-checker@0.1.13: @@ -4262,7 +4268,7 @@ packages: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: true - /tsup@8.0.2(typescript@5.3.3): + /tsup@8.0.2(typescript@5.4.5): resolution: {integrity: sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==} engines: {node: '>=18'} hasBin: true @@ -4295,7 +4301,7 @@ packages: source-map: 0.8.0-beta.0 sucrase: 3.35.0 tree-kill: 1.2.2 - typescript: 5.3.3 + typescript: 5.4.5 transitivePeerDependencies: - supports-color - ts-node @@ -4339,8 +4345,8 @@ packages: engines: {node: '>=8'} dev: true - /typescript@5.3.3: - resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + /typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} hasBin: true dev: true @@ -4411,7 +4417,7 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /vite-node@1.4.0(@types/node@18.19.17): + /vite-node@1.4.0(@types/node@20.12.8): resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -4420,7 +4426,7 @@ packages: debug: 4.3.4(supports-color@8.1.1) pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.1.4(@types/node@18.19.17) + vite: 5.1.4(@types/node@20.12.8) transitivePeerDependencies: - '@types/node' - less @@ -4432,7 +4438,7 @@ packages: - terser dev: true - /vite@5.1.4(@types/node@18.19.17): + /vite@5.1.4(@types/node@20.12.8): resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -4460,7 +4466,7 @@ packages: terser: optional: true dependencies: - '@types/node': 18.19.17 + '@types/node': 20.12.8 esbuild: 0.19.12 postcss: 8.4.35 rollup: 4.12.0 @@ -4468,7 +4474,7 @@ packages: fsevents: 2.3.3 dev: true - /vitest@1.4.0(@types/node@18.19.17): + /vitest@1.4.0(@types/node@20.12.8): resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -4493,7 +4499,7 @@ packages: jsdom: optional: true dependencies: - '@types/node': 18.19.17 + '@types/node': 20.12.8 '@vitest/expect': 1.4.0 '@vitest/runner': 1.4.0 '@vitest/snapshot': 1.4.0 @@ -4511,8 +4517,8 @@ packages: strip-literal: 2.0.0 tinybench: 2.6.0 tinypool: 0.8.2 - vite: 5.1.4(@types/node@18.19.17) - vite-node: 1.4.0(@types/node@18.19.17) + vite: 5.1.4(@types/node@20.12.8) + vite-node: 1.4.0(@types/node@20.12.8) why-is-node-running: 2.2.2 transitivePeerDependencies: - less diff --git a/src/api/rpc.ts b/src/api/rpc.ts index 0157c474..18ad7ef8 100644 --- a/src/api/rpc.ts +++ b/src/api/rpc.ts @@ -32,7 +32,7 @@ export interface VitestPool extends VitestPoolMethods { export interface VitestEvents { onConsoleLog: (log: UserConsoleLog) => void onTaskUpdate: (task: TaskResultPack[]) => void - onFinished: (files?: File[], errors?: unknown[], collecting?: boolean) => void + onFinished: (files: File[], unhandledError: string, collecting?: boolean) => void onCollected: (files?: File[], collecting?: boolean) => void onWatcherStart: (files?: File[], errors?: unknown[], collecting?: boolean) => void onWatcherRerun: (files: string[], trigger?: string, collecting?: boolean) => void diff --git a/src/debug/debugManager.ts b/src/debug/debugManager.ts index 1897d1a7..9165c4c5 100644 --- a/src/debug/debugManager.ts +++ b/src/debug/debugManager.ts @@ -138,6 +138,10 @@ export class TestDebugManager extends vscode.Disposable { public async stopDebugging() { await Promise.allSettled([...this.sessions].map(([, s]) => vscode.debug.stopDebugging(s))) } + + public get enabled() { + return this.port !== undefined + } } interface TestDebugConfiguration { diff --git a/src/extension.ts b/src/extension.ts index f16c3941..d9a77102 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode' +import './polyfills' import { gte } from 'semver' import { version } from '../package.json' import { getConfig, testControllerId } from './config' @@ -139,7 +140,10 @@ class VitestExtension { ) } runProfile.tag = api.tag - runProfile.runHandler = (request, token) => runner.runTests(request, token) + runProfile.runHandler = async (request, token) => { + await runner.stopDebugRun() + await runner.runTests(request, token) + } this.runProfiles.set(`${api.id}:run`, runProfile) let debugProfile = previousRunProfiles.get(`${api.id}:debug`) if (!debugProfile) { @@ -179,7 +183,10 @@ class VitestExtension { ) } coverageProfile.tag = api.tag - coverageProfile.runHandler = (request, token) => runner.runCoverage(request, token) + coverageProfile.runHandler = async (request, token) => { + await runner.stopDebugRun() + await runner.runCoverage(request, token) + } coverageProfile.loadDetailedCoverage = coverageContext.loadDetailedCoverage this.runProfiles.set(`${api.id}:coverage`, coverageProfile) } diff --git a/src/polyfills.ts b/src/polyfills.ts new file mode 100644 index 00000000..ed697799 --- /dev/null +++ b/src/polyfills.ts @@ -0,0 +1,11 @@ +if (!Promise.withResolvers) { + Promise.withResolvers = function withResolvers() { + let a: (v: T | PromiseLike) => void + let b: (r?: any) => void + const c = new this((resolve, reject) => { + a = resolve + b = reject + }) + return { resolve: a!, reject: b!, promise: c } + } +} diff --git a/src/runner/runner.ts b/src/runner/runner.ts index 4e0aaf8f..509d8fe2 100644 --- a/src/runner/runner.ts +++ b/src/runner/runner.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode' import { getTasks } from '@vitest/ws-client' import type { ErrorWithDiff, File, ParsedStack, Task, TaskResult } from 'vitest' import { basename, normalize, relative } from 'pathe' -import { type TestData, TestFile, TestFolder, getTestData } from '../testTreeData' +import { TestFile, TestFolder, getTestData } from '../testTreeData' import type { TestTree } from '../testTree' import type { VitestFolderAPI } from '../api' import { log } from '../log' @@ -15,15 +15,13 @@ import { coverageContext, readCoverageReport } from '../coverage' export class TestRunner extends vscode.Disposable { private continuousRequests = new Set() - private simpleTestRunRequest: vscode.TestRunRequest | null = null - - // TODO: doesn't support "projects" - run every project because Vitest doesn't support - // granular filters yet (coming in Vitest 1.4.1) - private testRunsByFile = new Map() - private testRunsByRequest = new WeakMap() + private nonContinuousRequest: vscode.TestRunRequest | undefined private _onRequestsExhausted = new vscode.EventEmitter() + private testRun: vscode.TestRun | undefined + private testRunDefer: PromiseWithResolvers | undefined + constructor( private readonly controller: vscode.TestController, private readonly tree: TestTree, @@ -32,9 +30,11 @@ export class TestRunner extends vscode.Disposable { ) { super(() => { api.clearListeners() - this.testRunsByFile.forEach(run => run.end()) - this.testRunsByFile.clear() - this.simpleTestRunRequest = null + this.testRun?.end() + this.testRun = undefined + this.testRunDefer?.resolve() + this.testRunDefer = undefined + this.nonContinuousRequest = undefined this.continuousRequests.clear() this.api.cancelRun() this._onRequestsExhausted.dispose() @@ -49,7 +49,7 @@ export class TestRunner extends vscode.Disposable { log.error('Cannot find task during onTaskUpdate', testId) return } - const testRun = this.getTestRunByTestItem(test) + const testRun = this.testRun // there is no test run for collected tests if (!testRun) return @@ -71,7 +71,7 @@ export class TestRunner extends vscode.Disposable { log.error(`Test data not found for "${task.name}"`) return } - const testRun = this.getTestRunByTestItem(test) + const testRun = this.testRun if (!testRun) return if (task.mode === 'skip' || task.mode === 'todo') @@ -81,7 +81,11 @@ export class TestRunner extends vscode.Disposable { }) }) - api.onFinished(async (files = [], _, collecting) => { + api.onFinished(async (files = [], unhandledError, collecting) => { + const testRun = this.testRun + if (!testRun) + return + try { if (!collecting) await this.reportCoverage(files) @@ -90,29 +94,24 @@ export class TestRunner extends vscode.Disposable { showVitestError(`Failed to report coverage. ${err.message}`, err) } - const finishedRuns = new Set() - files.forEach((file) => { const testItem = this.tree.getTestItemByTask(file) - const data = testItem && getTestData(testItem) as TestFile | undefined - const testRun = data && this.getTestRunByData(data) - if (testRun && data) { + if (testItem) this.markResult(testRun, testItem, file.result, file) - this.testRunsByFile.delete(file.filepath) - if (!finishedRuns.has(testRun)) { - finishedRuns.add(testRun) - testRun.end() - } - } }) + + if (unhandledError) + testRun.appendOutput(formatTestOutput(unhandledError)) + + this.endTestRun() }) api.onConsoleLog(({ content, taskId }) => { const testItem = taskId ? tree.getTestItemByTaskId(taskId) : undefined - const testRun = testItem && this.getTestRunByTestItem(testItem) + const testRun = this.testRun if (testRun) { testRun.appendOutput( - content.replace(/(? [item, []] as [vscode.TestItem, never[]]) - this.simpleTestRunRequest = request + this.nonContinuousRequest = request token.onCancellationRequested(() => { this.debug.disable(this.api) - this.endRequestRuns(request) - this.simpleTestRunRequest = null + this.endTestRun() + this.nonContinuousRequest = undefined this.api.cancelRun() + // just in case it gets stuck + this.testRunDefer?.resolve() }) // we need to run tests one file at a time, so we partition them // it's important to keep the same test items that were in the original request // because they dictate how format testNamePattern for (const [testFileData, testFileChildren] of testItems) { - if (token.isCancellationRequested) + if (token.isCancellationRequested || !this.debug.enabled) break const includedTests = testFileChildren.length @@ -160,16 +161,15 @@ export class TestRunner extends vscode.Disposable { await this.debug.disable(this.api) - this.simpleTestRunRequest = null + this.nonContinuousRequest = undefined this._onRequestsExhausted.fire() } - private endRequestRuns(request: vscode.TestRunRequest) { - const runs = this.testRunsByRequest.get(request) - if (!runs) - return - runs.forEach(run => run.end()) - this.testRunsByRequest.delete(request) + private endTestRun() { + this.testRun?.end() + this.testRunDefer?.resolve() + this.testRun = undefined + this.testRunDefer = undefined } private async watchContinuousTests(request: vscode.TestRunRequest, token: vscode.CancellationToken) { @@ -177,24 +177,41 @@ export class TestRunner extends vscode.Disposable { token.onCancellationRequested(() => { this.continuousRequests.delete(request) - this.endRequestRuns(request) if (!this.continuousRequests.size) { this._onRequestsExhausted.fire() this.api.unwatchTests() + this.endTestRun() } }) if (!request.include?.length) { + log.info('[RUNNER]', 'Watching all test files') await this.api.watchTests() } else { const include = [...this.continuousRequests].map(r => r.include || []).flat() const files = getTestFiles(include) const testNamePatern = formatTestPattern(include) + log.info( + '[RUNNER]', + 'Watching test files:', + files.map(f => relative(this.api.workspaceFolder.uri.fsPath, f)).join(', '), + testNamePatern ? `with pattern ${testNamePatern}` : '', + ) await this.api.watchTests(files, testNamePatern) } } + public async stopDebugRun() { + // if debuggins session is active, users can still run tests + // there is no guarantee it will end correctly, so we nuke it + if (this.debug.enabled) { + await this.debug.disable(this.api) + this.nonContinuousRequest = undefined + this.endTestRun() + } + } + public async runCoverage(request: vscode.TestRunRequest, token: vscode.CancellationToken) { try { await this.api.enableCoverage() @@ -205,7 +222,7 @@ export class TestRunner extends vscode.Disposable { } const { dispose } = this._onRequestsExhausted.event(() => { - if (!this.continuousRequests.size && !this.simpleTestRunRequest) { + if (!this.continuousRequests.size && !this.nonContinuousRequest) { this.api.disableCoverage() dispose() } @@ -224,21 +241,25 @@ export class TestRunner extends vscode.Disposable { if (request.continuous) return await this.watchContinuousTests(request, token) - this.simpleTestRunRequest = request + this.nonContinuousRequest = request token.onCancellationRequested(() => { - this.endRequestRuns(request) - this.simpleTestRunRequest = null + this.endTestRun() + this.nonContinuousRequest = undefined this.api.cancelRun() }) await this.runTestItems(request, request.include || []) - this.simpleTestRunRequest = null + this.nonContinuousRequest = undefined this._onRequestsExhausted.fire() } private async runTestItems(request: vscode.TestRunRequest, tests: readonly vscode.TestItem[]) { + await this.testRunDefer?.promise + + this.testRunDefer = Promise.withResolvers() + const runTests = (files?: string[], testNamePatern?: string) => 'updateSnapshots' in request ? this.api.updateSnapshots(files, testNamePatern) @@ -260,21 +281,6 @@ export class TestRunner extends vscode.Disposable { } } - private getTestRunByTestItem(data: vscode.TestItem) { - return this.getTestRunByData(getTestData(data)) - } - - private getTestRunByData(data: TestData): vscode.TestRun | null { - if (data instanceof TestFolder) - return null - if (data instanceof TestFile) - return this.testRunsByFile.get(data.filepath) || null - - if ('file' in data) - return this.getTestRunByData(data.file) - return null - } - private isFileIncluded(file: string, include: readonly vscode.TestItem[] | vscode.TestItemCollection) { for (const _item of include) { const item = 'id' in _item ? _item : _item[1] @@ -321,24 +327,27 @@ export class TestRunner extends vscode.Disposable { ) } - private async startTestRun(files: string[], primaryRun?: vscode.TestRun, primaryRequest?: vscode.TestRunRequest) { - const request = primaryRequest || this.simpleTestRunRequest || this.createContinuousRequest() + private async startTestRun(files: string[], primaryRequest?: vscode.TestRunRequest) { + const request = primaryRequest || this.nonContinuousRequest || this.createContinuousRequest() if (!request) return + if (this.testRun) { + await this.testRunDefer?.promise + this.endTestRun() + } + const name = files.length > 1 ? undefined : relative(this.api.workspaceFolder.uri.fsPath, files[0]) - const run = primaryRun || this.controller.createTestRun(request, name) - const testRunsByRequest = this.testRunsByRequest.get(request) || [] - this.testRunsByRequest.set(request, [...testRunsByRequest, run]) + const run = this.testRun = this.controller.createTestRun(request, name) for (const file of files) { if (file[file.length - 1] === '/') { const files = this.getTestFilesInFolder(file) - this.startTestRun(files, run, request) + this.startTestRun(files, request) continue } @@ -346,18 +355,12 @@ export class TestRunner extends vscode.Disposable { if (request.include && !this.isFileIncluded(file, request.include)) continue - const testRun = this.testRunsByFile.get(file) - if (testRun) - continue - const testItems = this.tree.getFileTestItems(file) function enqueue(test: vscode.TestItem) { run.enqueued(test) test.children.forEach(enqueue) } testItems.forEach(test => enqueue(test)) - - this.testRunsByFile.set(file, run) } } @@ -371,15 +374,10 @@ export class TestRunner extends vscode.Disposable { const coverage = readCoverageReport(reportsDirectory) - const runs = new Set() - - const promises = files.map(async (file) => { - const testItem = this.tree.getTestItemByTask(file) - const testRun = testItem && this.getTestRunByTestItem(testItem) - if (testRun && !runs.has(testRun)) { - runs.add(testRun) + const promises = files.map(async () => { + const testRun = this.testRun + if (testRun) await coverageContext.applyJson(testRun, coverage) - } }) await Promise.all(promises) @@ -494,7 +492,7 @@ function getFolderFiles(folder: vscode.TestItem): vscode.TestItem[] { return files } -function partitionTestFileItems(tree: TestTree, tests: readonly vscode.TestItem[]) { +function partitionTestFileItems(tests: readonly vscode.TestItem[]) { const fileItems = new Map() for (const testItem of tests) { @@ -560,3 +558,7 @@ function formatTestPattern(tests: readonly vscode.TestItem[]) { return undefined return patterns.join('|') } + +function formatTestOutput(output: string) { + return output.replace(/(? { - this.rpc.onFinished(this.meta.id, files, errors, collecting) + this.rpc.onFinished(this.meta.id, files || [], output, collecting) }) } diff --git a/src/worker/setupFile.ts b/src/worker/setupFile.ts index 2e110f87..dd514178 100644 --- a/src/worker/setupFile.ts +++ b/src/worker/setupFile.ts @@ -9,6 +9,16 @@ const testFile = workerState.filepath! // don't run tests that are not watched if rerun was triggered - only collect those tests if (rerunTriggered) { - if (!watchEveryFile && !continuousFiles.includes(testFile)) + if (!watchEveryFile && !testFileWatched()) workerState.config.testNamePattern = /$a/ } + +function testFileWatched() { + return continuousFiles.some((file) => { + if (file === testFile) + return true + if (file[file.length - 1] === '/') + return testFile.startsWith(file) + return false + }) +} diff --git a/src/worker/watcher.ts b/src/worker/watcher.ts index cd05305d..cdf36604 100644 --- a/src/worker/watcher.ts +++ b/src/worker/watcher.ts @@ -1,4 +1,5 @@ import type { ProvidedContext, Vitest as VitestCore } from 'vitest' +import { relative } from 'pathe' import { Vitest } from './vitest' export class VitestWatcher { @@ -41,29 +42,58 @@ export class VitestWatcher { } ctx.configOverride.testNamePattern = new RegExp(Vitest.COLLECT_NAME_PATTERN) + ctx.logger.log('Collecting tests due to file changes:', ...files.map(f => relative(ctx.config.root, f))) return await originalScheduleRerun.call(this, files) } state.rerunTriggered = true - ctx.configOverride.testNamePattern = state.testNamePattern ? new RegExp(state.testNamePattern) : undefined - if (state.watchEveryFile) + const namePattern = state.testNamePattern ? new RegExp(state.testNamePattern) : undefined + ctx.configOverride.testNamePattern = namePattern + if (state.watchEveryFile) { + ctx.logger.log( + 'Rerunning all tests due to file changes:', + ...files.map(f => relative(ctx.config.root, f)), + namePattern ? `with pattern ${namePattern}` : '', + ) return await originalScheduleRerun.call(this, files) + } if (!isTestFileTrigger) { // if souce code is changed and related tests are not continious, remove them from changedTests const updatedTests = new Set() for (const file of this.changedTests) { - if (state.files.includes(file)) + if (state.isTestFileWatched(file)) updatedTests.add(file) } this.changedTests = updatedTests } + if (this.changedTests.size) { + ctx.logger.log( + 'Rerunning tests due to file changes:', + ...[...this.changedTests].map(f => relative(ctx.config.root, f)), + namePattern ? `with pattern ${namePattern}` : '', + ) + } + return await originalScheduleRerun.call(this, files) } } + private isTestFileWatched(testFile: string) { + if (!this.files?.length) + return false + + return this.files.some((file) => { + if (file === testFile) + return true + if (file[file.length - 1] === '/') + return testFile.startsWith(file) + return false + }) + } + markRerun(rerun: boolean) { this.rerunTriggered = rerun }