diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..dc9d507 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +root = true + +[*] +insert_final_newline = true +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/.esbuild.js b/.esbuild.js index 664198b..356ac91 100644 --- a/.esbuild.js +++ b/.esbuild.js @@ -4,14 +4,15 @@ const watch = process.argv.includes("--watch"); const minify = watch ? process.argv.includes("--minify") : !process.argv.includes("--no-minify"); const ctx = esbuild.context({ - entryPoints: ["src/extension.ts"], + entryPoints: ["src/extension.ts", "src/reporter/fullJsonStreamReporter.ts"], tsconfig: "./tsconfig.json", bundle: true, - external: ["vscode", "pnpapi"], + external: ["vscode", "pnpapi", "mocha", "esbuild"], sourcemap: !minify, minify, platform: "node", outdir: "out", + keepNames: !minify }); ctx diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 160fa0f..f0e7db0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,15 +5,24 @@ on: pull_request: workflow_dispatch: +permissions: + contents: read + actions: read + checks: write + jobs: build: name: Build and Test strategy: + fail-fast: false matrix: os: [macos-latest, ubuntu-latest, windows-latest] vscode-version: ['stable', 'insiders'] vscode-platform: ['desktop'] runs-on: ${{ matrix.os }} + env: + VSCODE_TEST_VERSION: ${{matrix.vscode-version}} + VSCODE_TEST_PLATFORM: ${{matrix.vscode-platform}} steps: - name: Checkout uses: actions/checkout@v4 @@ -22,7 +31,18 @@ jobs: with: node-version: lts/* - run: npm install + - run: npm run compile:test + - name: Print Test Config + run: node ./.vscode-test.mjs + env: + VSCODE_CONFIG_LOG: true - run: xvfb-run -a npm test if: runner.os == 'Linux' - run: npm test if: runner.os != 'Linux' + - uses: dorny/test-reporter@v1 + if: always() + with: + name: VS Code Test Results (${{matrix.os}}, ${{matrix.vscode-version}}, ${{matrix.vscode-platform}}) + path: 'test-results/*.json' + reporter: mocha-json diff --git a/.gitignore b/.gitignore index d9af962..70839a0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* +test-results/*.json # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json @@ -121,6 +122,7 @@ dist # Stores VSCode versions used for testing VSCode extensions .vscode-test +.working-copy # yarn v2 .yarn/cache diff --git a/.vscode-ci-test-reporter.js b/.vscode-ci-test-reporter.js new file mode 100644 index 0000000..5096146 --- /dev/null +++ b/.vscode-ci-test-reporter.js @@ -0,0 +1,19 @@ +const BaseReporter = require('mocha/lib/reporters/base'); +const SpecReporter = require('mocha/lib/reporters/spec'); +const JsonReporter = require('mocha/lib/reporters/json'); + +module.exports = class MultiReporter extends BaseReporter { + reporters; + + constructor(runner, options) { + super(runner, options); + this.reporters = [ + new SpecReporter(runner, { + reporterOption: options.reporterOption.specReporterOption, + }), + new JsonReporter(runner, { + reporterOption: options.reporterOption.jsonReporterOption, + }), + ]; + } +}; diff --git a/.vscode-test.mjs b/.vscode-test.mjs index be4f0fb..b8caac9 100644 --- a/.vscode-test.mjs +++ b/.vscode-test.mjs @@ -4,26 +4,68 @@ import * as path from 'path'; import { fileURLToPath } from 'url'; const dirname = fileURLToPath(new URL('.', import.meta.url)); -const testCaseRunnerDir = path.join(dirname, 'out/test/testCases'); +const integrationTestDir = path.join(dirname, 'out/test/integration'); +const workspaceBaseDir = path.join(dirname, 'test-workspaces'); -// @ts-check +const vsCodeVersion = process.env.VSCODE_TEST_VERSION ?? 'stable'; +const vsCodePlatform = process.env.VSCODE_TEST_PLATFORM ?? 'desktop'; -export default defineConfig([ +let createCommonOptions = (label) => { + if (process.env.GITHUB_ACTIONS) { + return { + platform: vsCodePlatform, + version: vsCodeVersion, + env: { + MOCHA_COLORS: 'true', + }, + mocha: { + ui: 'bdd', + + reporter: path.join(dirname, '.vscode-ci-test-reporter.js'), + reporterOption: { + jsonReporterOption: { + output: path.join(dirname, 'test-results', `${label}.json`), + }, + }, + timeout: 60_000, + }, + }; + } else { + return { + platform: vsCodePlatform, + version: vsCodeVersion, + + mocha: { + ui: 'bdd', + timeout: 60_000, + }, + }; + } +}; + +const config = [ { - label: 'core', - files: 'out/**/*.test.js', - mocha: { ui: 'bdd' }, + label: 'unit', + files: 'out/test/unit/**/*.test.js', + ...createCommonOptions('unit'), }, ...fs - .readdirSync(testCaseRunnerDir) - .filter((f) => f.endsWith('.js')) + .readdirSync(integrationTestDir) + .filter((f) => f.endsWith('.test.js')) .map((file) => { - const label = path.basename(file, '.js'); + const label = path.basename(file, '.test.js'); return { label, - files: path.join(testCaseRunnerDir, file), - workspaceFolder: path.join(dirname, `testCases/${label}`), - mocha: { ui: 'bdd', timeout: 60_000 }, + files: path.join(integrationTestDir, file), + workspaceFolder: path.join(workspaceBaseDir, label), + ...createCommonOptions(label), }; }), -]); +]; + +if (process.env.VSCODE_CONFIG_LOG) { + const util = await import('util'); + console.log(util.inspect(config, { showHidden: false, depth: null, colors: true })); +} + +export default defineConfig(config); diff --git a/.vscode/settings.json b/.vscode/settings.json index 4c64e48..f3281a5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,6 +10,14 @@ "typescript.tsc.autoDetect": "off", "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.organizeImports": "explicit" + "source.organizeImports": "never", + "source.sortMembers": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[github-actions-workflow]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" } -} +} \ No newline at end of file diff --git a/.vscodeignore b/.vscodeignore index d5fa914..743ddba 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -13,3 +13,8 @@ out/test/** testCases/** .esbuild.js .nvmrc + + +node_modules/** +!node_modules/esbuild/**/* +!package.json \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index f9ba8cf..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,9 +0,0 @@ -# Microsoft Open Source Code of Conduct - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). - -Resources: - -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5a78e0c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,27 @@ +# Contributing Guide + +This project was scaffolded with the Yeoman and VS Code Extension generator. +https://code.visualstudio.com/api/get-started/your-first-extension + + +## Get up and running straight away + +* Press `F5` to open a new window with the extension loaded. +* Set breakpoints in the code inside `src/extension.ts` to debug the extension. +* Find output from the extension in the debug console. + + +## Make changes + +* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. +* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with the extension to load your changes. + +## Run tests (of this extension) + +* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner) +* Run the "watch" task via the **Tasks: Run Task** command. Make sure this is running, or tests might not be discovered. +* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A` +* See the output of the test result in the Test Results view. +* Make changes to `src/test/extension.test.ts` or create new test files inside the `test` folder. + * The provided test runner will only consider files matching the name pattern `**.test.ts`. + * You can create folders inside the `test` folder to structure your tests any way you want. diff --git a/LICENSE b/LICENSE index 9e841e7..b8e5a1c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ MIT License + Copyright (c) Daniel Kuschny (Danielku15) and contributors. Copyright (c) Microsoft Corporation. Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/README.md b/README.md index e19abd1..33a5b4d 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,38 @@ -# VS Code Extension Test Runner +# Mocha VS Code Extension -This is a VS Code extension for other extension authors, that runs tests as you develop extensions. It requires use of the new extension test API and configuration file. For more information, see our [testing guide for extension authors](https://code.visualstudio.com/api/working-with-extensions/testing-extension). +This is the Mocha extension for VS Code enabling developers to run and debug tests right within VS Code using the built-in test explorer. + +> [!NOTE] +> This extension is in a fairly early development stage but mostly functional. We soon +> will start to publish some pre-release versions to the VS Code Extension gallery. +> +> Follow our progress at https://github.com/orgs/CoderLine/projects/15/views/1 +> +> Please provide feedback and discuss improvements over at https://github.com/CoderLine/mocha-vscode/discussions + +## Credits + +This project started as a fork of the `Extension Test Runner` and `Command-line runner for VS Code tests` developed by Microsoft and then was adapted to work with Mocha directly. +The main credits of this extension go over to the folks at Microsoft (and their contributors) and without them it would have been a lot more effort to ship a Mocha test runner for VS Code. + +- https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner +- https://github.com/microsoft/vscode-extension-test-runner +- https://github.com/microsoft/vscode-test-cl ## Getting Started -Please follow the [testing guide for extension authors](https://code.visualstudio.com/api/working-with-extensions/testing-extension) to initially set up tests using the command line. Then, [install this extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner). +Please follow the [general Mocha documentation](https://mochajs.org/) to initially set up tests using the command line. Then, [install this extension](https://marketplace.visualstudio.com/items?itemName=mocha.mocha-vscode). -This extension automatically discovers and works with the `.vscode-test.js/mjs/cjs` files found in your workspace. It requires minimal to no extra configuration. It works by looking at test files in your JavaScript code. If you write tests in TypeScript, you will want to: +This extension automatically discovers and works with the `.mocharc.js/cjs/yaml/yml/json/jsonc` files found in your workspace. It requires minimal to no extra configuration. It works by looking at test files in your JavaScript code. If you write tests in TypeScript, you will want to: 1. Modify your tsconfig.json and add `"sourceMap": true` -1. Add `**/*.js.map` to your `.vscodeignore` file to avoid bloating the published extension. ## Configuration -- `extension-test-runner.extractSettings`: configures how tests get extracted. You can configure: +- `mocha-vscode.extractSettings`: configures how tests get extracted. You can configure: - The `extractWith` mode, that specifies if tests are extracted via evaluation or syntax-tree parsing. Evaluation is likely to lead to better results, but may have side-effects. Defaults to `evaluation`. + - The `extractTimeout` limiting how long the extraction of tests for a single file is allowed to take. - The `test` and `suite` identifiers the process extracts. Defaults to `["it", "test"]` and `["describe", "suite"]` respectively, covering Mocha's common interfaces. -- `extension-test-runner.debugOptions`: options, normally found in the launch.json, to pass when debugging the extension. See [the docs](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configuration-attributes) for a complete list of options. - -## Contributing - -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. - -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - -## Trademarks - -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft -trademarks or logos is subject to and must follow -[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). -Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. -Any use of third-party trademarks or logos are subject to those third-party's policies. +- `mocha-vscode.debugOptions`: options, normally found in the launch.json, to pass when debugging the extension. See [the docs](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configuration-attributes) for a complete list of options. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 82db58a..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,41 +0,0 @@ - - -## Security - -Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). - -If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. - -## Reporting Security Issues - -**Please do not report security vulnerabilities through public GitHub issues.** - -Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). - -If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). - -You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). - -Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: - - * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) - * Full paths of source file(s) related to the manifestation of the issue - * The location of the affected source code (tag/branch/commit or direct URL) - * Any special configuration required to reproduce the issue - * Step-by-step instructions to reproduce the issue - * Proof-of-concept or exploit code (if possible) - * Impact of the issue, including how an attacker might exploit the issue - -This information will help us triage your report more quickly. - -If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. - -## Preferred Languages - -We prefer all communications to be in English. - -## Policy - -Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). - - diff --git a/SUPPORT.md b/SUPPORT.md deleted file mode 100644 index 836a8d3..0000000 --- a/SUPPORT.md +++ /dev/null @@ -1,25 +0,0 @@ -# TODO: The maintainer of this repo has not yet edited this file - -**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? - -- **No CSS support:** Fill out this template with information about how to file issues and get help. -- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. -- **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. - -*Then remove this first heading from this SUPPORT.MD file before publishing your repo.* - -# Support - -## How to file issues and get help - -This project uses GitHub Issues to track bugs and feature requests. Please search the existing -issues before filing new issues to avoid duplicates. For new issues, file your bug or -feature request as a new Issue. - -For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE -FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER -CHANNEL. WHERE WILL YOU HELP PEOPLE?**. - -## Microsoft Support Policy - -Support for this **PROJECT or PRODUCT** is limited to the resources listed above. diff --git a/icon.png b/icon.png index 8176d40..e78b6c6 100644 Binary files a/icon.png and b/icon.png differ diff --git a/package-lock.json b/package-lock.json index 20338ca..7c80911 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,25 +1,30 @@ { - "name": "extension-test-runner", - "version": "0.0.7", + "name": "mocha-vscode", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "extension-test-runner", - "version": "0.0.7", + "name": "mocha-vscode", + "version": "1.0.0", "hasInstallScript": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.19", + "@types/which": "^3.0.3", + "@typescript-eslint/typescript-estree": "^7.3.1", "acorn-loose": "^8.3.0", "ansi-colors": "^4.1.3", + "c8": "^9.1.0", "data-uri-to-buffer": "^6.0.1", "enhanced-resolve": "^5.15.0", "error-stack-parser": "^2.1.4", "eslint-visitor-keys": "^3.4.3", - "istanbul-to-vscode": "^1.0.1", "minimatch": "^9.0.3", "split2": "^4.2.0", - "stacktrace-parser": "^0.1.10" + "stacktrace-parser": "^0.1.10", + "supports-color": "^9.4.0", + "which": "^4.0.0", + "yargs": "^17.7.2" }, "devDependencies": { "@types/chai": "^4.3.7", @@ -30,7 +35,9 @@ "@types/picomatch": "^2.3.1", "@types/sinon": "^10.0.19", "@types/split2": "^4.2.1", - "@vscode/test-cli": "^0.0.4", + "@types/yargs": "^17.0.32", + "@vscode/dts": "^0.4.0", + "@vscode/test-cli": "^0.0.8", "@vscode/test-electron": "^2.3.9", "acorn": "^8.10.0", "chai": "^4.3.10", @@ -39,17 +46,38 @@ "mocha": "^10.2.0", "prettier": "^3.0.3", "sinon": "^16.1.0", - "typescript": "^5.2.2", - "vscode-dts": "^0.3.3" + "tsx": "^4.7.1", + "typescript": "^5.2.2" }, "engines": { "vscode": "^1.83.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/android-arm": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.4.tgz", - "integrity": "sha512-uBIbiYMeSsy2U0XQoOGVVcpIktjLMEKa7ryz2RLr7L/vTnANNEsPVAh4xOv7ondGz6ac1zVb0F8Jx20rQikffQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", "cpu": [ "arm" ], @@ -63,9 +91,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.4.tgz", - "integrity": "sha512-mRsi2vJsk4Bx/AFsNBqOH2fqedxn5L/moT58xgg51DjX1la64Z3Npicut2VbhvDFO26qjWtPMsVxCd80YTFVeg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", "cpu": [ "arm64" ], @@ -79,9 +107,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.4.tgz", - "integrity": "sha512-4iPufZ1TMOD3oBlGFqHXBpa3KFT46aLl6Vy7gwed0ZSYgHaZ/mihbYb4t7Z9etjkC9Al3ZYIoOaHrU60gcMy7g==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", "cpu": [ "x64" ], @@ -95,9 +123,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.4.tgz", - "integrity": "sha512-Lviw8EzxsVQKpbS+rSt6/6zjn9ashUZ7Tbuvc2YENgRl0yZTktGlachZ9KMJUsVjZEGFVu336kl5lBgDN6PmpA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", "cpu": [ "arm64" ], @@ -111,9 +139,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.4.tgz", - "integrity": "sha512-YHbSFlLgDwglFn0lAO3Zsdrife9jcQXQhgRp77YiTDja23FrC2uwnhXMNkAucthsf+Psr7sTwYEryxz6FPAVqw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", "cpu": [ "x64" ], @@ -127,9 +155,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.4.tgz", - "integrity": "sha512-vz59ijyrTG22Hshaj620e5yhs2dU1WJy723ofc+KUgxVCM6zxQESmWdMuVmUzxtGqtj5heHyB44PjV/HKsEmuQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", "cpu": [ "arm64" ], @@ -143,9 +171,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.4.tgz", - "integrity": "sha512-3sRbQ6W5kAiVQRBWREGJNd1YE7OgzS0AmOGjDmX/qZZecq8NFlQsQH0IfXjjmD0XtUYqr64e0EKNFjMUlPL3Cw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", "cpu": [ "x64" ], @@ -159,9 +187,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.4.tgz", - "integrity": "sha512-z/4ArqOo9EImzTi4b6Vq+pthLnepFzJ92BnofU1jgNlcVb+UqynVFdoXMCFreTK7FdhqAzH0vmdwW5373Hm9pg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", "cpu": [ "arm" ], @@ -175,9 +203,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.4.tgz", - "integrity": "sha512-ZWmWORaPbsPwmyu7eIEATFlaqm0QGt+joRE9sKcnVUG3oBbr/KYdNE2TnkzdQwX6EDRdg/x8Q4EZQTXoClUqqA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", "cpu": [ "arm64" ], @@ -191,9 +219,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.4.tgz", - "integrity": "sha512-EGc4vYM7i1GRUIMqRZNCTzJh25MHePYsnQfKDexD8uPTCm9mK56NIL04LUfX2aaJ+C9vyEp2fJ7jbqFEYgO9lQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", "cpu": [ "ia32" ], @@ -207,9 +235,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.4.tgz", - "integrity": "sha512-WVhIKO26kmm8lPmNrUikxSpXcgd6HDog0cx12BUfA2PkmURHSgx9G6vA19lrlQOMw+UjMZ+l3PpbtzffCxFDRg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", "cpu": [ "loong64" ], @@ -223,9 +251,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.4.tgz", - "integrity": "sha512-keYY+Hlj5w86hNp5JJPuZNbvW4jql7c1eXdBUHIJGTeN/+0QFutU3GrS+c27L+NTmzi73yhtojHk+lr2+502Mw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", "cpu": [ "mips64el" ], @@ -239,9 +267,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.4.tgz", - "integrity": "sha512-tQ92n0WMXyEsCH4m32S21fND8VxNiVazUbU4IUGVXQpWiaAxOBvtOtbEt3cXIV3GEBydYsY8pyeRMJx9kn3rvw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", "cpu": [ "ppc64" ], @@ -255,9 +283,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.4.tgz", - "integrity": "sha512-tRRBey6fG9tqGH6V75xH3lFPpj9E8BH+N+zjSUCnFOX93kEzqS0WdyJHkta/mmJHn7MBaa++9P4ARiU4ykjhig==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", "cpu": [ "riscv64" ], @@ -271,9 +299,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.4.tgz", - "integrity": "sha512-152aLpQqKZYhThiJ+uAM4PcuLCAOxDsCekIbnGzPKVBRUDlgaaAfaUl5NYkB1hgY6WN4sPkejxKlANgVcGl9Qg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", "cpu": [ "s390x" ], @@ -287,9 +315,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.4.tgz", - "integrity": "sha512-Mi4aNA3rz1BNFtB7aGadMD0MavmzuuXNTaYL6/uiYIs08U7YMPETpgNn5oue3ICr+inKwItOwSsJDYkrE9ekVg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", "cpu": [ "x64" ], @@ -303,9 +331,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.4.tgz", - "integrity": "sha512-9+Wxx1i5N/CYo505CTT7T+ix4lVzEdz0uCoYGxM5JDVlP2YdDC1Bdz+Khv6IbqmisT0Si928eAxbmGkcbiuM/A==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", "cpu": [ "x64" ], @@ -319,9 +347,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.4.tgz", - "integrity": "sha512-MFsHleM5/rWRW9EivFssop+OulYVUoVcqkyOkjiynKBCGBj9Lihl7kh9IzrreDyXa4sNkquei5/DTP4uCk25xw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", "cpu": [ "x64" ], @@ -335,9 +363,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.4.tgz", - "integrity": "sha512-6Xq8SpK46yLvrGxjp6HftkDwPP49puU4OF0hEL4dTxqCbfx09LyrbUj/D7tmIRMj5D5FCUPksBbxyQhp8tmHzw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", "cpu": [ "x64" ], @@ -351,9 +379,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.4.tgz", - "integrity": "sha512-PkIl7Jq4mP6ke7QKwyg4fD4Xvn8PXisagV/+HntWoDEdmerB2LTukRZg728Yd1Fj+LuEX75t/hKXE2Ppk8Hh1w==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", "cpu": [ "arm64" ], @@ -367,9 +395,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.4.tgz", - "integrity": "sha512-ga676Hnvw7/ycdKB53qPusvsKdwrWzEyJ+AtItHGoARszIqvjffTwaaW3b2L6l90i7MO9i+dlAW415INuRhSGg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", "cpu": [ "ia32" ], @@ -383,9 +411,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.4.tgz", - "integrity": "sha512-HP0GDNla1T3ZL8Ko/SHAS2GgtjOg+VmWnnYLhuTksr++EnduYB0f3Y2LzHsUwb2iQ13JGoY6G3R8h6Du/WG6uA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "cpu": [ "x64" ], @@ -415,6 +443,14 @@ "node": ">=12" } }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", @@ -437,6 +473,38 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -578,133 +646,138 @@ "@types/node": "*" } }, - "node_modules/@vscode/test-cli": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.4.tgz", - "integrity": "sha512-Tx0tfbxeSb2Xlo+jpd+GJrNLgKQHobhRHrYvOipZRZQYWZ82sKiK02VY09UjU1Czc/YnZnqyAnjUfaVGl3h09w==", + "node_modules/@types/which": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.3.tgz", + "integrity": "sha512-2C1+XoY0huExTbs8MQv1DuS5FS86+SEjdM9F/+GS61gg5Hqbtj8ZiDSx8MfWcyei907fIPbfPGCOrNUTnVHY1g==" + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dev": true, "dependencies": { - "@types/mocha": "^10.0.2", - "chokidar": "^3.5.3", - "glob": "^10.3.10", - "minimatch": "^9.0.3", - "mocha": "^10.2.0", - "supports-color": "^9.4.0", - "yargs": "^17.7.2" - }, - "bin": { - "vscode-test": "out/bin.mjs" + "@types/yargs-parser": "*" } }, - "node_modules/@vscode/test-cli/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true }, - "node_modules/@vscode/test-cli/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, + "node_modules/@typescript-eslint/types": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.3.1.tgz", + "integrity": "sha512-2tUf3uWggBDl4S4183nivWQ2HqceOZh1U4hhu4p1tPiIJoRRXrab7Y+Y0p+dozYwZVvLPRI6r5wKe9kToF9FIw==", "engines": { - "node": ">=12" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@vscode/test-cli/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/@vscode/test-cli/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.3.1.tgz", + "integrity": "sha512-tLpuqM46LVkduWP7JO7yVoWshpJuJzxDOPYIVWUUZbW+4dBpgGeUdl/fQkhuV0A8eGnphYw3pp8d2EnvPOfxmQ==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "@typescript-eslint/types": "7.3.1", + "@typescript-eslint/visitor-keys": "7.3.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@vscode/test-cli/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.3.1.tgz", + "integrity": "sha512-9RMXwQF8knsZvfv9tdi+4D/j7dMG28X/wMJ8Jj6eOHyHWwDW4ngQJcqEczSsqIKKjFiLFr40Mnr7a5ulDD3vmw==", "dependencies": { - "ansi-regex": "^5.0.1" + "@typescript-eslint/types": "7.3.1", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@vscode/test-cli/node_modules/supports-color": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", - "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "node_modules/@vscode/dts": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@vscode/dts/-/dts-0.4.0.tgz", + "integrity": "sha512-m28fZnyS9PlzVvYHppyC3Q98U2RFIZO2srnBMvyupPBY5QkSxoNIjTV9roLaU7kE+gc+HXczH8XHPETUkt9IAA==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "https-proxy-agent": "^7.0.0", + "minimist": "^1.2.8", + "prompts": "^2.4.2" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "bin": { + "dts": "index.js" } }, - "node_modules/@vscode/test-cli/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/@vscode/dts/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "debug": "^4.3.4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">= 14" } }, - "node_modules/@vscode/test-cli/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "node_modules/@vscode/dts/node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "dev": true, "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "agent-base": "^7.0.2", + "debug": "4" }, "engines": { - "node": ">=12" + "node": ">= 14" } }, - "node_modules/@vscode/test-cli/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "node_modules/@vscode/test-cli": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.8.tgz", + "integrity": "sha512-sBSMSDzJChiiDjmys2Q6uI4SIoUYf0t6oDsQO3ypaQ7udha9YD4e2On9e9VE5OBayZMpxbgJX+NudmCwJMdOIg==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "@types/mocha": "^10.0.2", + "c8": "^9.1.0", + "chokidar": "^3.5.3", + "enhanced-resolve": "^5.15.0", + "glob": "^10.3.10", + "minimatch": "^9.0.3", + "mocha": "^10.2.0", + "supports-color": "^9.4.0", + "yargs": "^17.7.2" + }, + "bin": { + "vscode-test": "out/bin.mjs" } }, "node_modules/@vscode/test-electron": { @@ -780,7 +853,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -810,6 +882,14 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -845,7 +925,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -859,6 +938,38 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "node_modules/c8": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", + "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=14.14.0" + } + }, + "node_modules/c8/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -957,21 +1068,22 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/cliui/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -979,14 +1091,12 @@ "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/cliui/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -1000,7 +1110,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -1012,7 +1121,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -1029,7 +1137,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1040,14 +1147,17 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/core-util-is": { "version": "1.0.3", @@ -1059,7 +1169,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1069,6 +1178,25 @@ "node": ">= 8" } }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/data-uri-to-buffer": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", @@ -1081,7 +1209,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -1127,6 +1254,17 @@ "node": ">=0.3.1" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -1160,9 +1298,9 @@ } }, "node_modules/esbuild": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.4.tgz", - "integrity": "sha512-x7jL0tbRRpv4QUyuDMjONtWFciygUxWaUM1kMX2zWxI0X2YWOt7MSA0g4UdeSiHM8fcYVzpQhKYOycZwxTdZkA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", "dev": true, "hasInstallScript": true, "bin": { @@ -1172,35 +1310,35 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.19.4", - "@esbuild/android-arm64": "0.19.4", - "@esbuild/android-x64": "0.19.4", - "@esbuild/darwin-arm64": "0.19.4", - "@esbuild/darwin-x64": "0.19.4", - "@esbuild/freebsd-arm64": "0.19.4", - "@esbuild/freebsd-x64": "0.19.4", - "@esbuild/linux-arm": "0.19.4", - "@esbuild/linux-arm64": "0.19.4", - "@esbuild/linux-ia32": "0.19.4", - "@esbuild/linux-loong64": "0.19.4", - "@esbuild/linux-mips64el": "0.19.4", - "@esbuild/linux-ppc64": "0.19.4", - "@esbuild/linux-riscv64": "0.19.4", - "@esbuild/linux-s390x": "0.19.4", - "@esbuild/linux-x64": "0.19.4", - "@esbuild/netbsd-x64": "0.19.4", - "@esbuild/openbsd-x64": "0.19.4", - "@esbuild/sunos-x64": "0.19.4", - "@esbuild/win32-arm64": "0.19.4", - "@esbuild/win32-ia32": "0.19.4", - "@esbuild/win32-x64": "0.19.4" + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" } }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -1228,11 +1366,33 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1244,7 +1404,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -1269,7 +1428,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -1284,8 +1442,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -1305,7 +1462,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -1319,6 +1475,18 @@ "node": "*" } }, + "node_modules/get-tsconfig": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", + "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", @@ -1345,7 +1513,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -1353,6 +1520,25 @@ "node": ">= 6" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -1362,7 +1548,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -1376,6 +1561,11 @@ "he": "bin/he" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, "node_modules/http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", @@ -1403,6 +1593,14 @@ "node": ">= 6" } }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "engines": { + "node": ">= 4" + } + }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -1413,7 +1611,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -1422,8 +1619,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -1441,7 +1637,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -1450,7 +1645,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -1459,7 +1653,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -1471,7 +1664,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -1504,17 +1696,55 @@ "dev": true }, "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "engines": { + "node": ">=16" + } }, - "node_modules/istanbul-to-vscode": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/istanbul-to-vscode/-/istanbul-to-vscode-1.0.1.tgz", - "integrity": "sha512-lH9uS30Kw2F10TC6EWtiFWy51J/JRu4qoKmSNft9Kw1BQzxs4bbcgDckJyX3zFngg+XLFNN6XWvGHzmhNVJZnQ==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.6" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/jackspeak": { @@ -1587,7 +1817,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -1638,6 +1867,40 @@ "node": "14 || >=16.14" } }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -1719,6 +1982,32 @@ "node": ">=6" } }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/mocha/node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -1779,11 +2068,86 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/nanoid": { "version": "3.3.3", @@ -1832,7 +2196,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -1841,7 +2204,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -1856,7 +2218,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -1877,7 +2238,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } @@ -1886,7 +2246,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -1895,7 +2254,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -1931,6 +2289,14 @@ "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -1944,7 +2310,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -1986,6 +2351,25 @@ "node": ">= 6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -2026,66 +2410,48 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "queue-microtask": "^1.2.2" } }, "node_modules/safe-buffer": { @@ -2098,7 +2464,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -2113,7 +2478,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -2140,7 +2504,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -2152,7 +2515,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -2161,7 +2523,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -2214,6 +2575,14 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -2356,15 +2725,11 @@ } }, "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/supports-color?sponsor=1" @@ -2378,11 +2743,62 @@ "node": ">=6" } }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -2390,6 +2806,36 @@ "node": ">=8.0" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tsx": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz", + "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==", + "dev": true, + "dependencies": { + "esbuild": "~0.19.10", + "get-tsconfig": "^4.7.2" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -2411,7 +2857,6 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2432,34 +2877,31 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/vscode-dts": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/vscode-dts/-/vscode-dts-0.3.3.tgz", - "integrity": "sha512-JfOsWL0NvfVw0UF9bcTjlv1Onz3Ted7cgpPWKWMnHGB+72t/tn8WFDeKLZO42l2k9KJq/NGS9rFC5gZbyI4FTg==", - "deprecated": "vscode-dts has been renamed to @vscode/dts. Install using @vscode/dts instead.", - "dev": true, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", "dependencies": { - "minimist": "^1.2.0", - "prompts": "^2.1.0", - "rimraf": "^3.0.0" + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" }, - "bin": { - "vscode-dts": "index.js" + "engines": { + "node": ">=10.12.0" } }, "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { - "node-which": "bin/node-which" + "node-which": "bin/which.js" }, "engines": { - "node": ">= 8" + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/workerpool": { @@ -2559,14 +3001,12 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } @@ -2574,25 +3014,23 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { @@ -2623,7 +3061,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2631,14 +3068,12 @@ "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -2652,7 +3087,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -2660,11 +3094,18 @@ "node": ">=8" } }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index 8ba075f..e7a192e 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,27 @@ { - "name": "extension-test-runner", - "displayName": "Extension Test Runner", - "description": "Runs tests in VS Code extensions", - "publisher": "ms-vscode", - "version": "0.0.7", + "name": "mocha-vscode", + "displayName": "Mocha Test Runner", + "description": "Run and debug Mocha tests right within VS Code.", + "publisher": "danielku15", + "version": "1.0.0", "icon": "icon.png", "engines": { "vscode": "^1.83.0" }, "keywords": [ - "vscode", + "mocha", "test", - "cli" - ], - "enabledApiProposals": [ - "testCoverage" + "bdd", + "tdd", + "tap", + "testing", + "chai", + "assertion", + "ava", + "jest", + "tape", + "jasmine", + "karma" ], "categories": [ "Testing" @@ -30,8 +37,8 @@ { "title": "Extension Test Runner", "properties": { - "extension-test-runner.extractSettings": { - "markdownDescription": "Configures how tests get extracted. You can configure:\n\n- The `extractWith` mode, that specifies if tests are extracted via evaluation or syntax-tree parsing.\n- The `test` and `suite` identifiers the process extracts.", + "mocha-vscode.extractSettings": { + "markdownDescription": "Configures how tests get extracted. You can configure:\n\n- The `extractWith` mode, that specifies if tests are extracted via evaluation or syntax-tree parsing.\n- The `extractTimeout` limiting how long the extraction of tests for a single file is allowed to take.\n- The `test` and `suite` identifiers the process extracts.", "type": "object", "properties": { "suite": { @@ -52,6 +59,9 @@ "evaluation", "syntax" ] + }, + "extractTimeout": { + "type": "number" } }, "default": { @@ -63,15 +73,17 @@ "it", "test" ], - "extractWith": "evaluation" + "extractWith": "evaluation", + "extractTimeout": 10000 }, "required": [ "suite", "test", - "extractWith" + "extractWith", + "extractTimeout" ] }, - "extension-test-runner.debugOptions": { + "mocha-vscode.debugOptions": { "type": "object", "additionalProperties": true, "markdownDescription": "Options, normally found in the launch.json, to pass when debugging the extension. See [the docs](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configuration-attributes) for a complete list of options." @@ -81,24 +93,25 @@ ] }, "activationEvents": [ - "workspaceContains:**/.vscode-test.{js,cjs,mjs}", - "onCommand:extension-test-runner.get-controllers-for-test" + "workspaceContains:**/.mocharc.{js,cjs,yaml,yml,json,jsonc}", + "onCommand:mocha-vscode.get-controllers-for-test" ], "repository": { "type": "git", - "url": "https://github.com/microsoft/vscode-test-runner-ext.git" + "url": "https://github.com/mocha/mocha-vscode.git" }, "bugs": { - "url": "https://github.com/microsoft/vscode-test-runner-ext/issues" + "url": "https://github.com/mocha/mocha-vscode/issues" }, - "homepage": "https://github.com/microsoft/vscode-test-runner-ext#readme", + "homepage": "https://github.com/mocha/mocha-vscode#readme", "main": "./out/extension.js", "scripts": { "package": "vsce package --no-dependencies", "vscode:prepublish": "npm run compile", - "postinstall": "cd src/typings && vscode-dts main && vscode-dts dev", + "postinstall": "cd src/typings && npx @vscode/dts main && npx @vscode/dts dev", "clean": "node -e \"fs.rmSync('out',{force:true,recursive:true})\"", - "compile": "npm run clean && node .esbuild.js --minify", + "compile": "npm run clean && tsc --noEmit && node .esbuild.js --minify", + "compile:test": "tsc", "watch:esbuild": "npm run clean && node .esbuild.js --watch", "watch": "npm run clean && tsc --watch", "test": "tsc && vscode-test", @@ -113,7 +126,9 @@ "@types/picomatch": "^2.3.1", "@types/sinon": "^10.0.19", "@types/split2": "^4.2.1", - "@vscode/test-cli": "^0.0.4", + "@types/yargs": "^17.0.32", + "@vscode/dts": "^0.4.0", + "@vscode/test-cli": "^0.0.8", "@vscode/test-electron": "^2.3.9", "acorn": "^8.10.0", "chai": "^4.3.10", @@ -122,8 +137,8 @@ "mocha": "^10.2.0", "prettier": "^3.0.3", "sinon": "^16.1.0", - "typescript": "^5.2.2", - "vscode-dts": "^0.3.3" + "tsx": "^4.7.1", + "typescript": "^5.2.2" }, "prettier": { "printWidth": 100, @@ -131,15 +146,20 @@ }, "dependencies": { "@jridgewell/trace-mapping": "^0.3.19", + "@types/which": "^3.0.3", + "@typescript-eslint/typescript-estree": "^7.3.1", "acorn-loose": "^8.3.0", "ansi-colors": "^4.1.3", + "c8": "^9.1.0", "data-uri-to-buffer": "^6.0.1", "enhanced-resolve": "^5.15.0", "error-stack-parser": "^2.1.4", "eslint-visitor-keys": "^3.4.3", - "istanbul-to-vscode": "^1.0.1", "minimatch": "^9.0.3", "split2": "^4.2.0", - "stacktrace-parser": "^0.1.10" + "stacktrace-parser": "^0.1.10", + "supports-color": "^9.4.0", + "which": "^4.0.0", + "yargs": "^17.7.2" } } diff --git a/pipeline.yml b/pipeline.yml deleted file mode 100644 index 247d883..0000000 --- a/pipeline.yml +++ /dev/null @@ -1,53 +0,0 @@ -resources: - repositories: - - repository: templates - type: github - name: microsoft/vscode-engineering - ref: main - endpoint: Monaco - -parameters: - - name: publishExtension - displayName: 🚀 Publish Extension - type: boolean - default: false - - name: runTests - displayName: 👩‍🔬 Run Tests - type: boolean - default: true - -extends: - template: azure-pipelines/extension/stable.yml@templates - parameters: - publishExtension: ${{ parameters.publishExtension }} - vscePackageArgs: --no-dependencies - ghCreateTag: false - cgIgnoreDirectories: 'testCases,.vscode-test' - buildSteps: - - script: npm install ci - displayName: Install dependencies - - - script: | - # For integration tests on Linux - /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & - echo ">>> Started xvfb" - displayName: Start xvfb - - - script: npm run test - displayName: Run Tests - condition: ${{ eq(parameters.runTests, true) }} - env: - DISPLAY: ':99.0' - - tsa: - enabled: true - options: - codebaseName: 'devdiv_$(Build.Repository.Name)' - serviceTreeID: '053e3ba6-924d-456c-ace0-67812c5ccc52' - instanceUrl: 'https://devdiv.visualstudio.com/defaultcollection' - projectName: 'DevDiv' - areaPath: 'DevDiv\\VS Code (compliance tracking only)\\Visual Studio Code Debugging Extensions' - notificationAliases: - - 'stbatt@microsoft.com' - - 'lszomoru@microsoft.com' - - 'copeet@microsoft.com' diff --git a/src/configValue.ts b/src/configValue.ts index febbbd9..7d1578b 100644 --- a/src/configValue.ts +++ b/src/configValue.ts @@ -1,3 +1,8 @@ +/*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + import * as vscode from 'vscode'; const sectionName = 'extension-test-runner'; diff --git a/src/configurationFile.ts b/src/configurationFile.ts index a9579a0..557bc76 100644 --- a/src/configurationFile.ts +++ b/src/configurationFile.ts @@ -1,35 +1,43 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ -import type { TestConfiguration } from '@vscode/test-cli'; + import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; import resolveModule from 'enhanced-resolve'; import * as fs from 'fs'; import { minimatch } from 'minimatch'; import * as path from 'path'; import * as vscode from 'vscode'; -import { cliPackageName } from './constants'; +import which from 'which'; import { DisposableStore } from './disposable'; -import { CliPackageMissing, ConfigProcessReadError, HumanError } from './errors'; - -export interface IResolvedConfiguration { - env: Record; - extensionTestsPath: string; - extensionDevelopmentPath: string; - config: TestConfiguration; - path: string; -} +import { HumanError } from './errors'; + +type OptionsModule = { + loadOptions(): IResolvedConfiguration; +}; -const resolver = resolveModule.ResolverFactory.createResolver({ - fileSystem: new resolveModule.CachedInputFileSystem(fs, 4000), - conditionNames: ['node', 'require', 'module'], -}); +type ConfigModule = { + findConfig(): string; +}; + +export type IResolvedConfiguration = Mocha.MochaOptions & { + _: string[] | undefined; + 'node-option': string[] | undefined; + ignore: string[] | undefined; +}; export class ConfigurationFile implements vscode.Disposable { private readonly ds = new DisposableStore(); private readonly didDeleteEmitter = this.ds.add(new vscode.EventEmitter()); private readonly didChangeEmitter = this.ds.add(new vscode.EventEmitter()); + private _resolver?: resolveModule.Resolver; + private _optionsModule?: OptionsModule; + private _configModule?: ConfigModule; + private _pathToNode?: string; + private _pathToMocha?: string; + /** Cached read promise, invalided on file change. */ private readPromise?: Promise; @@ -81,75 +89,95 @@ export class ConfigurationFile implements vscode.Disposable { this.readPromise = undefined; } - /** - * Spawns the test CLI associated with the configuration file using the - * given args. - */ - public async spawnCli(args: readonly string[]) { - const cliPath = await this.resolveCli(); - return await new Promise((resolve, reject) => { - const p = spawn(process.execPath, [cliPath, '--config', this.uri.fsPath, ...args], { - cwd: path.dirname(this.uri.fsPath), - env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' }, - }); - p.on('spawn', () => resolve(p)); - p.on('error', reject); - }); + async getPathToNode() { + // We cannot use process.execPath as this points to code.exe which is an electron application + // also with ELECTRON_RUN_AS_NODE this can lead to errors (e.g. with the --import option) + // we prefer to use the system level node + this._pathToNode ??= (await which('node', { nothrow: true })) ?? process.execPath; + return this._pathToNode; } - /** - * Spawns the test CLI associated with the configuration file using the - * given args, and captures its output. - */ - public async captureCliJson(args: readonly string[]) { - const p = await this.spawnCli(args); - return await new Promise((resolve, reject) => { - const output: Buffer[] = []; - p.stdout.on('data', (chunk) => output.push(chunk)); - p.stderr.on('data', (chunk) => output.push(chunk)); - p.on('error', reject); - p.on('close', (code) => { - const joined = Buffer.concat(output).toString(); - if (code !== 0) { - return reject(new ConfigProcessReadError(joined)); - } + async getMochaSpawnArgs(customArgs: readonly string[]): Promise { + this._pathToMocha ??= await this._resolveLocalMochaPath('/bin/mocha.js'); - try { - resolve(JSON.parse(joined)); - } catch { - reject(new ConfigProcessReadError(`invalid JSON: ${joined}`)); - } - }); - }); + return [ + await this.getPathToNode(), + this._pathToMocha, + '--config', + this.uri.fsPath, + ...customArgs, + ]; } - private async _read() { - const configs = await this.captureCliJson(['--list-configuration']); - return new ConfigurationList(this.uri, configs, this.wf); - } + private async _resolveLocalMochaPath(suffix?: string): Promise { + this._resolver ??= resolveModule.ResolverFactory.createResolver({ + fileSystem: new resolveModule.CachedInputFileSystem(fs, 4000), + conditionNames: ['node', 'require', 'module'], + }); - /** - * Resolves the path to the test runner CLI, a JavaScript file. - * @throws {HumanError} if the module isn't found - */ - public async resolveCli(suffix?: string) { return new Promise((resolve, reject) => - resolver.resolve( + this._resolver!.resolve( {}, - this.uri.fsPath, - suffix ? `${cliPackageName}/${suffix}` : cliPackageName, + path.dirname(this.uri.fsPath), + 'mocha' + (suffix ?? ''), {}, (err, res) => { if (err) { - reject(new CliPackageMissing(err)); + reject( + new HumanError( + `Could not find mocha in working directory '${path.dirname( + this.uri.fsPath, + )}', please install mocha to run tests.`, + ), + ); } else { - resolve(suffix ? (res as string) : path.join(path.dirname(res as string), 'bin.mjs')); + resolve(res as string); } }, ), ); } + private async _read() { + this._optionsModule ??= require( + await this._resolveLocalMochaPath('/lib/cli/options'), + ) as OptionsModule; + this._configModule ??= require( + await this._resolveLocalMochaPath('/lib/cli/config'), + ) as ConfigModule; + let config: IResolvedConfiguration; + + // need to change to the working dir for loading the config, + // TODO[mocha]: allow specifying the cwd in loadOptions() + const currentCwd = process.cwd(); + try { + process.chdir(path.dirname(this.uri.fsPath)); + + // we need to ensure a reload for javascript files + // as they are in the require cache https://github.com/mochajs/mocha/blob/e263c7a722b8c2fcbe83596836653896a9e0258b/lib/cli/config.js#L37 + const configFile = this._configModule.findConfig(); + try { + const resolved = require.resolve(configFile); + delete require.cache[resolved]; + } catch (e) { + // ignore + } + + config = this._optionsModule.loadOptions(); + } finally { + process.chdir(currentCwd); + } + + return new ConfigurationList(this.uri, config, this.wf); + } + + /** + * Resolves the path to the package.json. + */ + public resolvePackageJson() { + return path.join(path.dirname(this.uri.fsPath), 'package.json'); + } + /** @inheritdoc */ public dispose() { this.ds.dispose(); @@ -162,30 +190,51 @@ export class ConfigurationList { private readonly patterns: ( | { glob: false; value: string } | { glob: true; value: string; workspaceFolderRelativeGlob: string } - )[][]; + )[]; constructor( public readonly uri: vscode.Uri, - public readonly value: IResolvedConfiguration[], + public readonly value: IResolvedConfiguration, wf: vscode.WorkspaceFolder, ) { - this.patterns = value.map(({ config }) => { - const files = typeof config.files === 'string' ? [config.files] : config.files; - return files.map((f) => { - if (path.isAbsolute(f)) { - return { glob: false, value: path.normalize(f) }; - } else { - const cfgDir = path.dirname(this.uri.fsPath); - return { - glob: true, - value: toForwardSlashes(path.join(cfgDir, f)), - workspaceFolderRelativeGlob: toForwardSlashes( - path.join(path.relative(wf.uri.fsPath, cfgDir), f), - ), - }; - } - }); + let positional = value._; + if (!positional) { + positional = ['./test/*.{js,cjs,mjs}']; + } + + this.patterns = positional.map((f) => { + if (path.isAbsolute(f)) { + return { glob: false, value: path.normalize(f) }; + } else { + const cfgDir = path.dirname(this.uri.fsPath); + return { + glob: true, + value: toForwardSlashes(path.join(cfgDir, f)), + workspaceFolderRelativeGlob: toForwardSlashes( + path.join(path.relative(wf.uri.fsPath, cfgDir), f), + ), + }; + } }); + + if (value.ignore) { + this.patterns.push( + ...value.ignore.map((f) => { + if (path.isAbsolute(f)) { + return { glob: false as false, value: '!' + path.normalize(f) }; + } else { + const cfgDir = path.dirname(this.uri.fsPath); + return { + glob: true as true, + value: '!' + toForwardSlashes(path.join(cfgDir, f)), + workspaceFolderRelativeGlob: toForwardSlashes( + path.join(path.relative(wf.uri.fsPath, cfgDir), f), + ), + }; + } + }), + ); + } } /** @@ -195,17 +244,15 @@ export class ConfigurationList { public roughIncludedFiles() { const patterns = new Set(); const files = new Set(); - for (const patternList of this.patterns) { - for (const p of patternList) { - if (p.value.startsWith('!')) { - continue; - } + for (const p of this.patterns) { + if (p.value.startsWith('!')) { + continue; + } - if (p.glob) { - patterns.add(p.workspaceFolderRelativeGlob); - } else { - files.add(p.value); - } + if (p.glob) { + patterns.add(p.workspaceFolderRelativeGlob); + } else { + files.add(p.value); } } @@ -215,28 +262,20 @@ export class ConfigurationList { /** Gets the configs the given test file is included by, if any. */ public includesTestFile(uri: vscode.Uri) { const file = toForwardSlashes(uri.fsPath); - let indexes: number[] | undefined; - - for (const [i, patterns] of this.patterns.entries()) { - let matched = false; - for (let { glob, value } of patterns) { - let negated = false; - if (value.startsWith('!')) { - negated = true; - value = value.slice(1); - } + let matched = false; - if (glob ? minimatch(file, value) : value === path.normalize(file)) { - matched = !negated; - } + for (let { glob, value } of this.patterns.values()) { + let negated = false; + if (value.startsWith('!')) { + negated = true; + value = value.slice(1); } - if (matched) { - indexes ??= []; - indexes.push(i); + if (glob ? minimatch(file, value) : value === path.normalize(file)) { + matched = !negated; } } - return indexes; + return matched; } } diff --git a/src/constants.ts b/src/constants.ts index 74bad3c..301da2e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,19 +1,42 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ -import { ITestSymbols } from './extract'; +import path from 'path'; +import type { ITestSymbols } from './extract/types'; /** Pattern of files the CLI looks for */ -export const configFilePattern = '**/.vscode-test.{js,mjs,cjs}'; -/** Package name of the VS Code CLI */ -export const cliPackageName = '@vscode/test-cli'; +export const configFilePattern = '**/.mocharc.{js,cjs,yaml,yml,json,jsonc}'; export const defaultTestSymbols: ITestSymbols = { suite: ['describe', 'suite'], test: ['it', 'test'], extractWith: 'evaluation', + extractTimeout: 10_000, }; -export const showConfigErrorCommand = 'extension-test-runner.showConfigError'; -export const getControllersForTestCommand = 'extension-test-runner.get-controllers-for-test'; +export const showConfigErrorCommand = 'mocha-vscode.showConfigError'; +export const getControllersForTestCommand = 'mocha-vscode.get-controllers-for-test'; + +function equalsIgnoreCase(a: string, b: string) { + return a.localeCompare(b, undefined, { sensitivity: 'accent' }) === 0; +} + +export function isTypeScript(filePath: string) { + const ext = path.extname(filePath); + // TODO: configuration for this extension list? + return ( + equalsIgnoreCase(ext, '.ts') || + equalsIgnoreCase(ext, '.mts') || + equalsIgnoreCase(ext, '.tsx') || + equalsIgnoreCase(ext, '.cts') || + equalsIgnoreCase(ext, '.jsx') + ); +} + +export function isEsm(filePath: string, code: string): boolean { + const ext = path.extname(filePath); + // very basic detection + return equalsIgnoreCase(ext, '.mjs') || code.includes('import ') || code.includes('export '); +} diff --git a/src/controller.ts b/src/controller.ts index b276f2e..a45ed7f 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ @@ -59,12 +60,18 @@ export class Controller { } constructor( + private readonly logChannel: vscode.LogOutputChannel, public readonly ctrl: vscode.TestController, private readonly wf: vscode.WorkspaceFolder, private readonly smStore: SourceMapStore, configFileUri: vscode.Uri, private readonly runner: TestRunner, ) { + logChannel.info( + 'New Test Controller for workspace folder and config', + wf.uri.fsPath, + configFileUri.fsPath, + ); this.disposable.add(ctrl); this.configFile = this.disposable.add(new ConfigurationFile(configFileUri, wf)); this.onDidDelete = this.configFile.onDidDelete; @@ -92,6 +99,8 @@ export class Controller { await this.readConfig(); } + this.logChannel.debug('Syncing file', uri); + const includeViaConfigs = this.currentConfig?.includesTestFile(uri); if (!includeViaConfigs) { return; @@ -104,13 +113,15 @@ export class Controller { const previous = this.testsInFiles.get(uri.toString()); const hash = createHash('sha256').update(contents).digest().readInt32BE(0); if (hash === previous?.hash) { + this.logChannel.debug('Cache not changed skipping update ', uri); return; } let tree: IParsedNode[]; try { - tree = extract(contents, this.extractMode.value); + tree = await extract(this.logChannel, uri.fsPath, contents, this.extractMode.value); } catch (e) { + this.logChannel.error('Error while test extracting ', e); this.deleteFileTests(uri.toString()); return; } @@ -122,7 +133,6 @@ export class Controller { const smMaintainer = previous?.sourceMap ?? this.smStore.maintain(uri); const sourceMap = await smMaintainer.refresh(contents); - const tags = includeViaConfigs.map((c) => new vscode.TestTag(`${c}`)); const add = ( parent: vscode.TestItem, node: IParsedNode, @@ -132,7 +142,6 @@ export class Controller { let item = parent.children.get(node.name); if (!item) { item = this.ctrl.createTestItem(node.name, node.name, start.uri); - item.tags = tags; testMetadata.set(item, { type: node.kind === NodeKind.Suite ? ItemType.Suite : ItemType.Test, }); @@ -144,10 +153,10 @@ export class Controller { const seen = new Map(); for (const child of node.children) { const existing = seen.get(child.name); - const start = sourceMap.originalPositionFor(child.startLine, child.startColumn - 1); + const start = sourceMap.originalPositionFor(child.startLine, child.startColumn); const end = child.endLine !== undefined && child.endColumn !== undefined - ? sourceMap.originalPositionFor(child.endLine, child.endColumn - 1) + ? sourceMap.originalPositionFor(child.endLine, child.endColumn) : start; if (existing) { addDuplicateDiagnostic(start, existing); @@ -171,13 +180,12 @@ export class Controller { // a single describe/test is not split between different files. const newTestsInFile = new Map(); for (const node of tree) { - const start = sourceMap.originalPositionFor(node.startLine, node.startColumn - 1); + const start = sourceMap.originalPositionFor(node.startLine, node.startColumn); const end = node.endLine !== undefined && node.endColumn !== undefined - ? sourceMap.originalPositionFor(node.endLine, node.endColumn - 1) + ? sourceMap.originalPositionFor(node.endLine, node.endColumn) : start; - const file = last(this.getContainingItemsForFile(start.uri, { compiledFile: uri, tags }))! - .item!; + const file = last(this.getContainingItemsForFile(start.uri, { compiledFile: uri }))!.item!; diagnosticCollection.delete(start.uri); newTestsInFile.set(node.name, add(file, node, start, end)); } @@ -268,37 +276,30 @@ export class Controller { } /** Creates run profiles for each configuration in the extension tests */ - private applyRunHandlers(configs: ConfigurationList) { + private applyRunHandlers() { const oldRunHandlers = this.runProfiles; this.runProfiles = new Map(); - for (const [index, { config }] of configs.value.entries()) { - const originalName = config.label || `Config #${index + 1}`; - let name = originalName; - for (let i = 2; this.runProfiles.has(name); i++) { - name = `${originalName} #${i}`; - } + const originalName = `Mocha Config`; + let name = originalName; + for (let i = 2; this.runProfiles.has(name); i++) { + name = `${originalName} #${i}`; + } - const prev = oldRunHandlers.get(name); - if (prev) { - this.runProfiles.set(name, prev); - oldRunHandlers.delete(name); - continue; - } + const prev = oldRunHandlers.get(name); + if (prev) { + this.runProfiles.set(name, prev); + oldRunHandlers.delete(name); + return; + } - const run = this.runner.makeHandler(this.ctrl, this.configFile, index, false); - const debug = this.runner.makeHandler(this.ctrl, this.configFile, index, true); - const coverage = this.runner.makeHandler(this.ctrl, this.configFile, index, false, true); - const profiles = [ - this.ctrl.createRunProfile(name, vscode.TestRunProfileKind.Run, run, true), - this.ctrl.createRunProfile(name, vscode.TestRunProfileKind.Debug, debug, true), - this.ctrl.createRunProfile(name, vscode.TestRunProfileKind.Coverage, coverage, true), - ]; - for (const profile of profiles) { - profile.tag = new vscode.TestTag(`${index}`); - } + const run = this.runner.makeHandler(this.ctrl, this.configFile, false); + const debug = this.runner.makeHandler(this.ctrl, this.configFile, true); + const profiles = [ + this.ctrl.createRunProfile(name, vscode.TestRunProfileKind.Run, run, true), + this.ctrl.createRunProfile(name, vscode.TestRunProfileKind.Debug, debug, true), + ]; - this.runProfiles.set(name, profiles); - } + this.runProfiles.set(name, profiles); for (const profiles of oldRunHandlers.values()) { for (const profile of profiles) { @@ -317,7 +318,7 @@ export class Controller { } if (configs !== this.currentConfig) { - this.applyRunHandlers(configs); + this.applyRunHandlers(); this.currentConfig = configs; } diff --git a/src/coverage.ts b/src/coverage.ts deleted file mode 100644 index b952d2b..0000000 --- a/src/coverage.ts +++ /dev/null @@ -1,64 +0,0 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -import { randomUUID } from 'crypto'; -import { existsSync, promises as fs, mkdirSync } from 'fs'; -import { IstanbulCoverage } from 'istanbul-to-vscode'; -import { tmpdir } from 'os'; -import { join, resolve } from 'path'; -import * as vscode from 'vscode'; -import { ConfigurationFile } from './configurationFile'; - -export class Coverage { - private readonly targetDir = join(tmpdir(), `ext-coverage-${randomUUID()}`); - public readonly args = [ - '--coverage', - '--coverage-reporter', - 'json', - '--coverage-output', - this.targetDir, - ]; - - constructor(private readonly configFile: ConfigurationFile) { - mkdirSync(this.targetDir, { recursive: true }); - } - - public async finalize(run: vscode.TestRun) { - const coverageFile = join(this.targetDir, 'coverage-final.json'); - if (existsSync(coverageFile)) { - try { - const contents = JSON.parse(await fs.readFile(coverageFile, 'utf8')); - run.coverageProvider = new CoverageProvider(contents); - } catch (e) { - vscode.window.showWarningMessage(`Error parsing test coverage: ${e}`); - } - } else { - try { - // if there wasn't the expected coverage file, check if that's because - // their CLI version is too old - const packageJsonPath = resolve(await this.configFile.resolveCli(), '../../package.json'); - const { version } = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')); - if (/^0\.0\.[0-4]$/.test(version)) { - vscode.window.showInformationMessage( - `Your @vscode/test-cli version is ${version}. Please update to CLI version 0.0.5 or higher to enable coverage.`, - { modal: true }, - ); - } - } catch { - // ignored - } - } - - await fs.rm(this.targetDir, { recursive: true, force: true }); - } -} - -class CoverageProvider extends IstanbulCoverage { - public booleanCounts = true; - - override mapLocation(compiledUri: vscode.Uri, l: number, c: number) { - // V8/sourcemaps can sometimes result in negative l/c when converting to base1: - return super.mapLocation(compiledUri, Math.max(0, l), Math.max(0, c)); - } -} diff --git a/src/disposable.ts b/src/disposable.ts index 638a31e..87d2eb7 100644 --- a/src/disposable.ts +++ b/src/disposable.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/errors.ts b/src/errors.ts index 9b91c91..5259e9d 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,22 +1,14 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ /** Errors with a human-readable message. */ export class HumanError extends Error {} -export class CliPackageMissing extends HumanError { - constructor(innerError: Error) { - // "Can't resolve 'foo' in 'C:\Users\conno\Github\vscode-extension-test-ext'" - super( - `${innerError.message}. Try running 'npm install @vscode/test-cli', and then 'Refresh Tests'`, - ); - } -} - export class ConfigProcessReadError extends HumanError { constructor(output: string) { - super(`Could not read .vscode-test configuration: ${output}`); + super(`Could not read .mocharc configuration: ${output}`); } } @@ -25,3 +17,9 @@ export class TestProcessExitedError extends HumanError { super(`Test process exited with code ${code}`); } } + +export class EvaluationProcessExitedError extends HumanError { + constructor(code: number | null) { + super(`Evaluation process exited with code ${code}`); + } +} diff --git a/src/extension.ts b/src/extension.ts index 831bd21..5e30ec3 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,12 +1,13 @@ +/*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + import * as path from 'path'; import * as timers from 'timers/promises'; import * as vscode from 'vscode'; import { ConfigValue } from './configValue'; -import { - configFilePattern, - getControllersForTestCommand, - showConfigErrorCommand, -} from './constants'; +import { configFilePattern, getControllersForTestCommand } from './constants'; import { Controller } from './controller'; import { TestRunner } from './runner'; import { SourceMapStore } from './source-map-store'; @@ -18,13 +19,17 @@ const enum FolderSyncState { } export function activate(context: vscode.ExtensionContext) { + const logChannel = vscode.window.createOutputChannel('Mocha VS Code Extension', { log: true }); + const smStore = new SourceMapStore(); - const runner = new TestRunner(smStore, new ConfigValue('debugOptions', {})); + const runner = new TestRunner(logChannel, smStore, new ConfigValue('debugOptions', {})); let ctrls: Controller[] = []; let resyncState: FolderSyncState = FolderSyncState.Idle; const syncWorkspaceFolders = async () => { + logChannel.debug('Syncing workspace folders', resyncState); + if (resyncState === FolderSyncState.Syncing) { resyncState = FolderSyncState.ReSyncNeeded; } @@ -41,19 +46,15 @@ export function activate(context: vscode.ExtensionContext) { folders.map(async (folder) => { const files = await vscode.workspace.findFiles( new vscode.RelativePattern(folder, configFilePattern), + '**/node_modules/**', ); + + logChannel.debug('Checking workspace folder for config files', folder); + for (const file of files) { - const rel = path.relative(folder.uri.fsPath, path.dirname(file.fsPath)); - const ctrl = vscode.tests.createTestController( - file.toString(), - rel - ? folders.length > 1 - ? `Extension (${folder.name}: ${rel})` - : `Extension (${rel})` - : folder.name, - ); - - ctrls.push(new Controller(ctrl, folder, smStore, file, runner)); + const ctrl = vscode.tests.createTestController(file.toString(), file.fsPath); + + ctrls.push(new Controller(logChannel, ctrl, folder, smStore, file, runner)); } }), ); @@ -66,24 +67,6 @@ export function activate(context: vscode.ExtensionContext) { } }; - const openUntitledEditor = async (contents: string) => { - const untitledDoc = await vscode.workspace.openTextDocument({ content: contents }); - await vscode.window.showTextDocument(untitledDoc); - }; - - const showConfigError = async (configUriStr: string) => { - const configUri = vscode.Uri.parse(configUriStr); - const ctrl = ctrls.find((c) => c.configFile.uri.toString() === configUri.toString()); - try { - await ctrl?.configFile.read(); - } catch (e) { - await openUntitledEditor(String(e)); - return; - } - - vscode.window.showInformationMessage('No configuration error detected'); - }; - const initialSync = (async () => { // Workaround for vscode#179203 where findFiles doesn't work on startup. // This extension is only activated on workspaceContains, so we have pretty @@ -100,11 +83,11 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push( vscode.workspace.onDidChangeWorkspaceFolders(syncWorkspaceFolders), - vscode.commands.registerCommand(showConfigErrorCommand, showConfigError), vscode.commands.registerCommand(getControllersForTestCommand, () => initialSync.then(() => ctrls), ), new vscode.Disposable(() => ctrls.forEach((c) => c.dispose())), + logChannel, ); } diff --git a/src/extract/evaluate.ts b/src/extract/evaluate.ts index 1de359c..ba4db2d 100644 --- a/src/extract/evaluate.ts +++ b/src/extract/evaluate.ts @@ -1,19 +1,15 @@ +/*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping'; import * as errorParser from 'error-stack-parser'; +import { transform as esbuildTransform } from 'esbuild'; import * as vm from 'vm'; import { IParsedNode, ITestSymbols, NodeKind } from '.'; - -/** - * Note: the goal is not to sandbox test code (workspace trust is required - * for this extension) but rather to avoid side-effects from evaluation which - * are much more likely when other code is required. - */ -const replacedGlobals = new Set([ - // avoid side-effects: - 'require', - 'process', - // avoid printing to the console from tests: - 'console', -]); +import { isEsm, isTypeScript } from '../constants'; +import * as vscode from 'vscode'; /** * Honestly kind of amazed this works. We can use a Proxy as our globalThis @@ -27,7 +23,26 @@ const replacedGlobals = new Set([ * Since extension host tests are always common.js (at least for now) this * is also effective in stubbing require() so we know code is nicely isolated. */ -export const extractWithEvaluation = (code: string, symbols: ITestSymbols) => { + +export async function extractWithEvaluation( + logChannel: vscode.LogOutputChannel | undefined, + filePath: string, + code: string, + symbols: ITestSymbols, +) { + /** + * Note: the goal is not to sandbox test code (workspace trust is required + * for this extension) but rather to avoid side-effects from evaluation which + * are much more likely when other code is required. + */ + const replacedGlobals = new Set([ + // avoid side-effects: + 'require', + 'process', + // avoid printing to the console from tests: + 'console', + ]); + const stack: IParsedNode[] = [{ children: [] } as Partial as IParsedNode]; // A placeholder object that returns itself for all functions calls and method accesses. @@ -43,7 +58,11 @@ export const extractWithEvaluation = (code: string, symbols: ITestSymbols) => { set: () => true, }); - function makeTesterFunction(kind: NodeKind, directive?: string) { + function makeTesterFunction( + kind: NodeKind, + sourceMap?: TraceMap | undefined, + directive?: string, + ) { const fn = (name: string, callback: () => void) => { if (typeof name !== 'string' || typeof callback !== 'function') { return placeholder(); @@ -54,24 +73,52 @@ export const extractWithEvaluation = (code: string, symbols: ITestSymbols) => { return placeholder(); } - const startLine = frame.lineNumber; - const startColumn = frame.columnNumber || 1; + // + // On error stack and source maps we are working on 1-based postitions + let startLine = frame.lineNumber || 1; + let startColumn = frame.columnNumber || 1; // approximate the length of the test case: - const functionLines = String(callback).split('\n'); - const endLine = frame.lineNumber + functionLines.length - 1; - let endColumn = functionLines[functionLines.length - 1].length; + let functionLines = String(callback).split('\n'); + let endLine = startLine + functionLines.length - 1; + let endColumn = functionLines[functionLines.length - 1].length + 1; + + if (sourceMap) { + try { + const startMapped = originalPositionFor(sourceMap, { + line: startLine, + column: startColumn - 1, + }); + if (startMapped.line !== null) { + startLine = startMapped.line; + startColumn = startMapped.column + 1; // columns are 0-based in trace-mapping lib + } + const endMapped = originalPositionFor(sourceMap, { + line: endLine, + column: endColumn - 1, + }); + if (endMapped.line !== null) { + endLine = endMapped.line; + endColumn = endMapped.column + 1; // columns are 0-based in trace-mapping lib + } + } catch (e) { + console.error('error mapping source', e); + } + } + if (endLine === startLine) { endColumn = Number.MAX_SAFE_INTEGER; // assume it takes the entire line of a single-line test case + } else { + endColumn -= 1; } const node: IParsedNode = { name, kind, - startLine, - startColumn, - endLine, - endColumn, + startLine: startLine - 1, + startColumn: startColumn - 1, + endLine: endLine - 1, + endColumn: endColumn, children: [], }; if (directive) { @@ -81,7 +128,7 @@ export const extractWithEvaluation = (code: string, symbols: ITestSymbols) => { if (kind === NodeKind.Suite) { stack.push(node); try { - callback.call(placeholder()); + return callback.call(placeholder()); } catch (e) { node.error = e instanceof Error ? e.message : String(e); } finally { @@ -90,16 +137,40 @@ export const extractWithEvaluation = (code: string, symbols: ITestSymbols) => { } }; if (!directive) { - fn.skip = makeTesterFunction(kind, 'skip'); - fn.only = makeTesterFunction(kind, 'only'); + fn.skip = makeTesterFunction(kind, sourceMap, 'skip'); + fn.only = makeTesterFunction(kind, sourceMap, 'only'); } return fn; } + let sourceMap: TraceMap | undefined; + // transpile typescript or ESM via esbuild if needed + if (isTypeScript(filePath) || isEsm(filePath, code)) { + const result = await esbuildTransform(code, { + target: `node${process.versions.node.split('.')[0]}`, // target current runtime + sourcemap: true, // need source map for correct test positions + format: 'cjs', // vm.runInNewContext only supports CJS + sourcesContent: false, // optimize source maps + minifyWhitespace: false, + minify: false, + keepNames: true, // reduce CPU + sourcefile: filePath, // for auto-detection of the loader + platform: 'node', // we will evaluate here in node + loader: 'default', // use the default loader + }); + + code = result.code; + try { + sourceMap = new TraceMap(result.map, filePath); + } catch (e) { + logChannel?.error('Error parsing source map of TypeScript output', e); + } + } + // currently these are the same, but they might be different in the future? - const suiteFunction = makeTesterFunction(NodeKind.Suite); - const testFunction = makeTesterFunction(NodeKind.Test); + const suiteFunction = makeTesterFunction(NodeKind.Suite, sourceMap); + const testFunction = makeTesterFunction(NodeKind.Test, sourceMap); const contextObj = new Proxy({} as any, { get(target, prop, _receiver) { @@ -118,8 +189,8 @@ export const extractWithEvaluation = (code: string, symbols: ITestSymbols) => { }); vm.runInNewContext(code, contextObj, { - timeout: 1000, + timeout: symbols.extractTimeout, }); return stack[0].children; -}; +} diff --git a/src/extract/index.ts b/src/extract/index.ts index 8625279..3567495 100644 --- a/src/extract/index.ts +++ b/src/extract/index.ts @@ -1,42 +1,29 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ import { extractWithEvaluation } from './evaluate'; import { extractWithAst } from './syntax'; +import { ITestSymbols } from './types'; +import * as vscode from 'vscode'; -export interface IParsedNode { - name: string; - kind: NodeKind; - startLine: number; // base 1 - startColumn: number; // base 1 - endLine?: number; // base 1 - endColumn?: number; // base 1 - directive?: 'skip' | 'only' | string; - children: IParsedNode[]; - error?: string; -} +export * from './types'; -export interface ITestSymbols { - suite: readonly string[]; - test: readonly string[]; - extractWith: 'evaluation' | 'syntax'; -} - -export const enum NodeKind { - Suite, - Test, -} - -export const extract = (code: string, symbols: ITestSymbols) => { +export const extract = async ( + logChannel: vscode.LogOutputChannel, + filePath: string, + code: string, + symbols: ITestSymbols, +) => { if (symbols.extractWith === 'evaluation') { try { - return extractWithEvaluation(code, symbols); + return await extractWithEvaluation(logChannel, filePath, code, symbols); } catch (e) { console.warn('error evaluating, will fallback', e); // fall through } } - return extractWithAst(code, symbols); + return extractWithAst(filePath, code, symbols); }; diff --git a/src/extract/syntax.test.ts b/src/extract/syntax.test.ts deleted file mode 100644 index 5ea1812..0000000 --- a/src/extract/syntax.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -import { expect } from 'chai'; -import { NodeKind } from '.'; -import { defaultTestSymbols } from '../constants'; -import { extractWithAst } from './syntax'; - -describe('syntax', () => { - it('extracts basic suite', () => { - const src = extractWithAst( - `suite('hello', () => { - it('works', () => {}); - })`, - defaultTestSymbols, - ); - expect(src).to.deep.equal([ - { - name: 'hello', - startLine: 1, - kind: NodeKind.Suite, - startColumn: 1, - endColumn: 7, - endLine: 3, - children: [ - { - name: 'works', - kind: NodeKind.Test, - startLine: 2, - startColumn: 7, - endColumn: 28, - endLine: 2, - children: [], - }, - ], - }, - ]); - }); - - it('works with skip/only', () => { - const src = extractWithAst( - `suite('hello', () => { - it.only('a', ()=>{}); - it.skip('a', ()=>{}); - })`, - defaultTestSymbols, - ); - expect(src).to.deep.equal([ - { - name: 'hello', - kind: NodeKind.Suite, - startLine: 1, - startColumn: 1, - endColumn: 7, - endLine: 4, - children: [ - { - name: 'a', - kind: NodeKind.Test, - startLine: 2, - startColumn: 9, - endColumn: 29, - endLine: 2, - children: [], - directive: 'only', - }, - { - name: 'a', - kind: NodeKind.Test, - startLine: 3, - startColumn: 9, - endColumn: 29, - endLine: 3, - children: [], - directive: 'skip', - }, - ], - }, - ]); - }); - - it('stubs out requires and placeholds correctly', () => { - const src = extractWithAst( - `require("some invalid module").doing().other.things()`, - defaultTestSymbols, - ); - expect(src).to.deep.equal([]); - }); -}); diff --git a/src/extract/syntax.ts b/src/extract/syntax.ts index 230a86b..5f045d5 100644 --- a/src/extract/syntax.ts +++ b/src/extract/syntax.ts @@ -1,12 +1,15 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ -import type { Options } from 'acorn'; -import { parse } from 'acorn-loose'; +import { parse as esTreeParse, type TSESTreeOptions } from '@typescript-eslint/typescript-estree'; +import type { Options as AcornOptions } from 'acorn'; +import { parse as acornParse } from 'acorn-loose'; import * as evk from 'eslint-visitor-keys'; import { Node } from 'estree'; import { IParsedNode, ITestSymbols, NodeKind } from '.'; +import { isTypeScript } from '../constants'; const enum C { MemberExpression = 'MemberExpression', @@ -17,12 +20,16 @@ const enum C { Identifier = 'Identifier', } -export const acornOptions: Options = { +export const acornOptions: AcornOptions = { ecmaVersion: 'latest', locations: true, allowReserved: true, }; +export const esTreeOptions: TSESTreeOptions = { + jsDocParsingMode: 'none', +}; + const getStringish = (nameArg: Node | undefined): string | undefined => { if (nameArg?.type === C.Literal && typeof nameArg.value === 'string') { return nameArg.value; @@ -56,8 +63,11 @@ const traverse = ( visitor.leave(node); }; -export const extractWithAst = (text: string, symbols: ITestSymbols) => { - const ast = parse(text, acornOptions); +export const extractWithAst = (filePath: string, text: string, symbols: ITestSymbols) => { + // TODO: pull some parsing options from the input config (e.g. package.json or .tsconfig beside it) + const ast = isTypeScript(filePath) + ? (esTreeParse(text, esTreeOptions) as Node) + : (acornParse(text, acornOptions) as Node); const interestingName = (name: string) => symbols.suite.includes(name) @@ -99,10 +109,10 @@ export const extractWithAst = (text: string, symbols: ITestSymbols) => { const child: IParsedNode = { children: [], kind, - startLine: node.loc!.start.line, - startColumn: node.loc!.start.column + 1, - endLine: node.loc!.end.line, - endColumn: node.loc!.end.column + 1, + startLine: node.loc!.start.line - 1, + startColumn: node.loc!.start.column, + endLine: node.loc!.end.line - 1, + endColumn: node.loc!.end.column, name, }; if (directive) { diff --git a/src/extract/types.ts b/src/extract/types.ts new file mode 100644 index 0000000..0299527 --- /dev/null +++ b/src/extract/types.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +export interface IParsedNode { + name: string; + kind: NodeKind; + startLine: number; // base 1 + startColumn: number; // base 1 + endLine?: number; // base 1 + endColumn?: number; // base 1 + directive?: 'skip' | 'only' | string; + children: IParsedNode[]; + error?: string; +} + +export interface ITestSymbols { + suite: readonly string[]; + test: readonly string[]; + extractWith: 'evaluation' | 'syntax'; + extractTimeout: number; +} + +export const enum NodeKind { + Suite, + Test, +} diff --git a/src/iterable.ts b/src/iterable.ts index 64454b8..701f8cd 100644 --- a/src/iterable.ts +++ b/src/iterable.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/metadata.ts b/src/metadata.ts index afed870..bbe3905 100644 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ @@ -19,7 +20,6 @@ export const testMetadata = new WeakMap(); export interface ICreateOpts { compiledFile: vscode.Uri; - tags: vscode.TestTag[]; } /** Gets the test collection for a file of the given URI, descending from the root. */ @@ -47,7 +47,6 @@ export function* getContainingItemsForFile( filePath[i], uri.with({ path: filePath.slice(0, i + 1).join('/') }), ); - item.tags = createOpts.tags; testMetadata.set( item, i === filePath.length - 1 diff --git a/src/outputQueue.ts b/src/outputQueue.ts index 98a0fd0..c3792c3 100644 --- a/src/outputQueue.ts +++ b/src/outputQueue.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/reporter/fullJsonStreamReporter.ts b/src/reporter/fullJsonStreamReporter.ts new file mode 100644 index 0000000..733ff63 --- /dev/null +++ b/src/reporter/fullJsonStreamReporter.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as Mocha from 'mocha'; +import { inspect } from 'util'; +import { MochaEvent, MochaEventTuple } from './fullJsonStreamReporterTypes'; + +/** + * Similar to the mocha JSON stream, but includes additional information + * on failure and when tests run. Specifically, the mocha json-stream does not + * include unmangled expected versus actual results. + * + * Writes a superset of the data that json-stream normally would. + */ +module.exports = class FullJsonStreamReporter { + constructor(runner: Mocha.Runner) { + const total = runner.total; + runner.once(Mocha.Runner.constants.EVENT_RUN_BEGIN, () => + writeEvent([MochaEvent.Start, { total }]), + ); + runner.once(Mocha.Runner.constants.EVENT_RUN_END, () => writeEvent([MochaEvent.End, {}])); + + runner.on(Mocha.Runner.constants.EVENT_SUITE_BEGIN, (suite: Mocha.Suite) => + writeEvent([MochaEvent.SuiteStart, { path: suite.titlePath(), file: suite.file }]), + ); + runner.on(Mocha.Runner.constants.EVENT_TEST_BEGIN, (test: Mocha.Test) => + writeEvent([MochaEvent.TestStart, clean(test)]), + ); + runner.on(Mocha.Runner.constants.EVENT_TEST_PASS, (test) => + writeEvent([MochaEvent.Pass, clean(test)]), + ); + runner.on(Mocha.Runner.constants.EVENT_TEST_FAIL, (test, err) => { + writeEvent([ + MochaEvent.Fail, + { + ...clean(test), + actual: inspect(err.actual, { depth: 30 }), + expected: inspect(err.expected, { depth: 30 }), + err: err.message, + stack: err.stack || null, + }, + ]); + }); + } +}; + +function writeEvent(event: MochaEventTuple) { + console.log(JSON.stringify(event)); +} + +const clean = (test: Mocha.Test) => { + return { + path: test.titlePath(), + duration: test.duration, + currentRetry: (test as any).currentRetry(), + file: test.file, + speed: + !test.duration || test.duration < test.slow() / 2 + ? ('fast' as const) + : test.duration > test.slow() + ? ('slow' as const) + : ('medium' as const), + }; +}; diff --git a/src/reporter/fullJsonStreamReporterTypes.ts b/src/reporter/fullJsonStreamReporterTypes.ts new file mode 100644 index 0000000..bc79567 --- /dev/null +++ b/src/reporter/fullJsonStreamReporterTypes.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +export enum MochaEvent { + Start = 'start', + TestStart = 'testStart', + Pass = 'pass', + Fail = 'fail', + End = 'end', + SuiteStart = 'suiteStart', +} + +export interface IStartEvent { + total: number; +} + +export interface ITestStartEvent { + path: string[]; + currentRetry: number; + file?: string; +} + +export interface IPassEvent extends ITestStartEvent { + duration?: number; + speed: 'fast' | 'medium' | 'slow'; +} + +export interface IFailEvent extends IPassEvent { + err: string; + stack: string | null; + expected?: string; + actual?: string; +} + +export interface IEndEvent {} + +export interface ISuiteStartEvent { + path: string[]; + file?: string; +} + +export type MochaEventTuple = + | [MochaEvent.Start, IStartEvent] + | [MochaEvent.TestStart, ITestStartEvent] + | [MochaEvent.Pass, IPassEvent] + | [MochaEvent.Fail, IFailEvent] + | [MochaEvent.End, IEndEvent] + | [MochaEvent.SuiteStart, ISuiteStartEvent]; diff --git a/src/runner.ts b/src/runner.ts index 872caa6..769197e 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -1,23 +1,24 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ -import { IDesktopTestConfiguration, MochaEvent, MochaEventTuple } from '@vscode/test-cli'; import styles from 'ansi-styles'; import { randomUUID } from 'crypto'; +import * as path from 'path'; import split2 from 'split2'; import * as vscode from 'vscode'; import { ConfigValue } from './configValue'; -import { ConfigurationFile, IResolvedConfiguration } from './configurationFile'; -import { Coverage } from './coverage'; +import { ConfigurationFile } from './configurationFile'; import { DisposableStore } from './disposable'; import { TestProcessExitedError } from './errors'; import { ItemType, testMetadata } from './metadata'; import { OutputQueue } from './outputQueue'; +import { MochaEvent, MochaEventTuple } from './reporter/fullJsonStreamReporterTypes'; import { SourceMapStore } from './source-map-store'; +import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; interface ISpawnOptions { - configIndex: number; config: ConfigurationFile; args: string[]; onLine: (line: string) => void; @@ -31,6 +32,7 @@ export type RunHandler = ( export class TestRunner { constructor( + private readonly logChannel: vscode.LogOutputChannel, private readonly smStore: SourceMapStore, private readonly launchConfig: ConfigValue>, ) {} @@ -38,23 +40,15 @@ export class TestRunner { public makeHandler( ctrl: vscode.TestController, config: ConfigurationFile, - configIndex: number, debug: boolean, - recordCoverage = false, ): RunHandler { return async (request) => { - const run = ctrl.createTestRun(request); - const baseArgs = ['--label', `${configIndex}`]; - let coverage: Coverage | undefined; - if (recordCoverage) { - coverage = new Coverage(config); - baseArgs.push(...coverage.args); - } + this.logChannel.debug('Creating new test run ', request); + const run = ctrl.createTestRun(request); const { args, compiledFileTests, leafTests } = await this.prepareArguments( ctrl, - config, - baseArgs, + [], request, run, ); @@ -77,7 +71,6 @@ export class TestRunner { run.token.onCancellationRequested(() => spawnCts.cancel()); const spawnOpts: ISpawnOptions = { - configIndex, args, config, onLine: (line) => { @@ -191,9 +184,9 @@ export class TestRunner { }; run.appendOutput( - `${styles.inverse.open} > ${styles.inverse.close} vscode-test ${spawnOpts.args.join( - ' ', - )}\r\n`, + `${styles.inverse.open} > ${styles.inverse.close} ${( + await config.getMochaSpawnArgs(spawnOpts.args) + ).join(' ')}}\r\n`, ); try { @@ -203,6 +196,8 @@ export class TestRunner { await this.runWithoutDebug(spawnOpts); } } catch (e) { + const errorMessage = e instanceof Error ? e : `Error executing tests ${e}`; + this.logChannel.error(errorMessage); if (!spawnCts.token.isCancellationRequested) { enqueueLine(String(e)); } @@ -225,9 +220,8 @@ export class TestRunner { } if (!ranAnyTest) { + this.logChannel.debug('No tests ran, show error'); await vscode.commands.executeCommand('testing.showMostRecentOutput'); - } else { - await coverage?.finalize(run); } await outputQueue.drain(); @@ -235,15 +229,12 @@ export class TestRunner { }; } - private async runDebug({ args, config, onLine, token, configIndex }: ISpawnOptions) { - const thisConfig = ( - await config.captureCliJson([...args, '--list-configuration']) - )?.[0]; - if (token.isCancellationRequested || !thisConfig || token.isCancellationRequested) { - return; - } - + private async runDebug({ args, config, onLine, token }: ISpawnOptions) { const ds = new DisposableStore(); + + const spawnArgs = await config.getMochaSpawnArgs(args); + this.logChannel.debug('Start test debugging with args', spawnArgs); + return new Promise((resolve, reject) => { const sessionKey = randomUUID(); const includedSessions = new Set(); @@ -252,16 +243,13 @@ export class TestRunner { Promise.resolve( vscode.debug.startDebugging(config.wf, { ...launchConfig, - type: 'extensionHost', + type: 'node', request: 'launch', - name: thisConfig.config.label || `Extension Test Config #${configIndex + 1}`, - args: [ - `--extensionTestsPath=${thisConfig.extensionTestsPath}`, - `--extensionDevelopmentPath=${thisConfig.extensionDevelopmentPath}`, - ...(launchConfig.args || []), - ...((thisConfig.config as IDesktopTestConfiguration).launchArgs || []), - ], - env: { ...thisConfig.env, ...launchConfig.env }, + name: `Mocha Test (${config.uri.fsPath})`, + program: spawnArgs[1], + args: [...spawnArgs.slice(2), ...(launchConfig.args || [])], + env: { ...launchConfig.env }, + __extensionSessionKey: sessionKey, }), ).catch(reject); @@ -324,11 +312,24 @@ export class TestRunner { }, }), ); - }).finally(() => ds.dispose()); + }).finally(() => { + ds.dispose(); + }); } private async runWithoutDebug({ args, config, onLine, token }: ISpawnOptions) { - const cli = await config.spawnCli(args); + const spawnArgs = await config.getMochaSpawnArgs(args); + this.logChannel.debug('Start test execution with args', spawnArgs); + + const cli = await new Promise((resolve, reject) => { + const p = spawn(spawnArgs[0], spawnArgs.slice(1), { + cwd: path.dirname(config.uri.fsPath), + env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' }, + }); + p.on('spawn', () => resolve(p)); + p.on('error', reject); + }); + if (token.isCancellationRequested) { return cli.kill(); } @@ -355,12 +356,11 @@ export class TestRunner { */ private async prepareArguments( ctrl: vscode.TestController, - config: ConfigurationFile, baseArgs: ReadonlyArray, request: vscode.TestRunRequest, run: vscode.TestRun, ) { - const reporter = await config.resolveCli('fullJsonStream'); + const reporter = path.resolve(__dirname, 'reporter', 'fullJsonStreamReporter.js'); const args = [...baseArgs, '--reporter', reporter]; const exclude = new Set(request.exclude); const leafTests = new Set(); @@ -401,6 +401,7 @@ export class TestRunner { } // if there's no include, omit --run so that every file is executed + // TODO[mocha]: expose an "--include" variant which allows limiting the tests to individual files independent from the loaded config if (!request.include || !exclude.size) { for (const path of compiledFileTests.value.keys()) { args.push('--run', path); @@ -427,6 +428,8 @@ class CompiledFileTests { * this is not present and we need to iterate file in the controller. */ public lookup(file: string | undefined, path: readonly string[]) { + file = this.sanitizePath(file); + if (file) { const items = this.value.get(file); return items && this.getPathInTestItems(items, path); @@ -456,14 +459,30 @@ class CompiledFileTests { } /** Associated a test with the given file path. */ - public push(path: string, test: vscode.TestItem) { - let set = this.value.get(path); + public push(file: string, test: vscode.TestItem) { + file = this.sanitizePath(file)!; + + let set = this.value.get(file); if (!set) { - this.value.set(path, (set = new Set())); + this.value.set(file, (set = new Set())); } set.add(test); } + + private sanitizePath(file: string | undefined): string | undefined { + if (file === undefined) { + return undefined; + } + + // on windows paths are case insensitive and we sometimes get inconsistent + // casings (e.g. C:\ vs c:\) - happens especially on debugging + if (process.platform === 'win32') { + return file.toLowerCase(); + } + + return file; + } } const getFullName = (test: vscode.TestItem) => { diff --git a/src/source-map-store.ts b/src/source-map-store.ts index e67e860..4277427 100644 --- a/src/source-map-store.ts +++ b/src/source-map-store.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/source-map.ts b/src/source-map.ts index 729ce5c..5ebf112 100644 --- a/src/source-map.ts +++ b/src/source-map.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ @@ -12,7 +13,7 @@ const smUrlComment = '//# sourceMappingURL='; export interface IMappingAccessor { /** - * @param line base-1 line + * @param line base-0 line * @param col base-0 column */ originalPositionFor(line: number, col: number): vscode.Location; @@ -20,17 +21,19 @@ export interface IMappingAccessor { export const identityMapping = (file: vscode.Uri): IMappingAccessor => ({ originalPositionFor(line, col) { - // VS Code positions are base 0, adjust the line - return new vscode.Location(file, new vscode.Position(line - 1, col)); + return new vscode.Location(file, new vscode.Position(line, col)); }, }); const smMappingAccessor = (file: vscode.Uri, sm: TraceMap): IMappingAccessor => ({ originalPositionFor(line, column) { - const { source, line: smLine, column: smCol } = originalPositionFor(sm, { line, column }); + const { + source, + line: smLine, + column: smCol, + } = originalPositionFor(sm, { line: line + 1, column: column }); if (!source) { - // VS Code positions are base 0, adjust the line - return new vscode.Location(file, new vscode.Position(line - 1, column)); + return new vscode.Location(file, new vscode.Position(line, column)); } return new vscode.Location(vscode.Uri.parse(source), new vscode.Position(smLine - 1, smCol)); diff --git a/src/test/integration/simple.test.ts b/src/test/integration/simple.test.ts new file mode 100644 index 0000000..8d084fd --- /dev/null +++ b/src/test/integration/simple.test.ts @@ -0,0 +1,206 @@ +/*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { promises as fs } from 'fs'; +import * as path from 'path'; +import { setTimeout } from 'timers/promises'; +import * as vscode from 'vscode'; +import { + captureTestRun, + expectTestTree, + getController, + integrationTestPrepare, + onceChanged, +} from '../util'; + +describe('simple', () => { + const workspaceFolder = integrationTestPrepare('simple'); + + it('discovers tests', async () => { + const c = await getController(); + + await expectTestTree(c, [ + ['folder', [['nested.test.js', [['is nested']]]]], + ['goodbye.test.js', [['math', [['division']]]]], + ['hello.test.js', [['math', [['addition'], ['subtraction']]]]], + ]); + }); + + it('handles file delete', async () => { + const c = await getController(); + const onChange = onceChanged(c); + + await fs.rm(path.join(workspaceFolder, 'hello.test.js')); + await onChange; + + await expectTestTree(c, [ + ['folder', [['nested.test.js', [['is nested']]]]], + ['goodbye.test.js', [['math', [['division']]]]], + ]); + }); + + it('cleans up folder if all child files are deleted', async () => { + const c = await getController(); + const onChange = onceChanged(c); + + await fs.rm(path.join(workspaceFolder, 'folder/nested.test.js')); + await onChange; + + await expectTestTree(c, [ + ['goodbye.test.js', [['math', [['division']]]]], + ['hello.test.js', [['math', [['addition'], ['subtraction']]]]], + ]); + }); + + it('handles file change', async () => { + const c = await getController(); + const onChange = onceChanged(c); + + await fs.writeFile( + path.join(workspaceFolder, 'hello.test.js'), + ` + test("subtraction", () => { + strictEqual(1 - 2, -1); + }); + `, + ); + await onChange; + + await expectTestTree(c, [ + ['folder', [['nested.test.js', [['is nested']]]]], + ['goodbye.test.js', [['math', [['division']]]]], + ['hello.test.js', [['subtraction']]], + ]); + }); + + it('runs tests', async () => { + const c = await getController(); + const profiles = c.profiles; + expect(profiles).to.have.lengthOf(2); + + const run = await captureTestRun( + c, + new vscode.TestRunRequest( + undefined, + undefined, + profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), + ), + ); + + run.expectStates({ + 'goodbye.test.js/math/division': ['enqueued', 'started', 'passed'], + 'hello.test.js/math/addition': ['enqueued', 'started', 'passed'], + 'hello.test.js/math/subtraction': ['enqueued', 'started', 'passed'], + 'folder/nested.test.js/is nested': ['enqueued', 'started', 'passed'], + }); + }); + + it('runs tests in directory', async () => { + const c = await getController(); + const run = await captureTestRun( + c, + new vscode.TestRunRequest( + [c.ctrl.items.get('folder')!], + undefined, + c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), + ), + ); + + run.expectStates({ + 'folder/nested.test.js/is nested': ['enqueued', 'started', 'passed'], + }); + }); + + it('runs tests in a file', async () => { + const c = await getController(); + const run = await captureTestRun( + c, + new vscode.TestRunRequest( + [c.ctrl.items.get('hello.test.js')!], + undefined, + c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), + ), + ); + + run.expectStates({ + 'hello.test.js/math/addition': ['enqueued', 'started', 'passed'], + 'hello.test.js/math/subtraction': ['enqueued', 'started', 'passed'], + }); + }); + + it('debugs tests in a file', async () => { + const c = await getController(); + const run = await captureTestRun( + c, + new vscode.TestRunRequest( + [c.ctrl.items.get('hello.test.js')!], + undefined, + c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Debug), + ), + ); + + run.expectStates({ + 'hello.test.js/math/addition': ['enqueued', 'started', 'passed'], + 'hello.test.js/math/subtraction': ['enqueued', 'started', 'passed'], + }); + }); + + it('runs subsets of tests', async () => { + const c = await getController(); + const run = await captureTestRun( + c, + new vscode.TestRunRequest( + [c.ctrl.items.get('hello.test.js')!.children.get('math')!.children.get('addition')!], + undefined, + c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), + ), + ); + + run.expectStates({ + 'hello.test.js/math/addition': ['enqueued', 'started', 'passed'], + }); + }); + + it('handles file and directory excludes', async () => { + const c = await getController(); + const run = await captureTestRun( + c, + new vscode.TestRunRequest( + undefined, + [c.ctrl.items.get('hello.test.js')!, c.ctrl.items.get('folder')!], + c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), + ), + ); + + run.expectStates({ + 'goodbye.test.js/math/division': ['enqueued', 'started', 'passed'], + }); + }); + + it('handles changes to .mocharc.js', async () => { + const c = await getController(); + const onChange = onceChanged(c); + + const configPath = path.join(workspaceFolder, '.mocharc.js'); + const original = await fs.readFile(configPath, 'utf-8'); + let updated = original.replace('**/*.test.js', '*.test.js'); + + // the vscode file watcher is set up async and does not always catch the change, keep changing the file + while (true) { + updated += '\n//'; + await fs.writeFile(configPath, updated); + const ok = await Promise.race([onChange.then(() => true), setTimeout(500)]); + if (ok) { + break; + } + } + + await expectTestTree(c, [ + ['goodbye.test.js', [['math', [['division']]]]], + ['hello.test.js', [['math', [['addition'], ['subtraction']]]]], + ]); + }); +}); diff --git a/src/test/integration/source-mapped.test.ts b/src/test/integration/source-mapped.test.ts new file mode 100644 index 0000000..4ab0008 --- /dev/null +++ b/src/test/integration/source-mapped.test.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import * as vscode from 'vscode'; +import { captureTestRun, expectTestTree, extractParsedNodes, getController } from '../util'; +import { NodeKind } from '../../extract'; + +describe('source mapped', () => { + it('discovers tests', async () => { + const c = await getController(); + + await expectTestTree(c, [['hello.test.ts', [['math', [['addition'], ['subtraction']]]]]]); + }); + + it('has correct test locations', async () => { + const c = await getController(); + + const src = extractParsedNodes(c.ctrl.items); + expect(src).to.deep.equal([ + { + name: 'hello.test.ts', + kind: NodeKind.Suite, + startLine: -1, + startColumn: -1, + endColumn: -1, + endLine: -1, + children: [ + { + name: 'math', + kind: NodeKind.Suite, + startLine: 2, + startColumn: 0, + endColumn: 1, + endLine: 10, + children: [ + { + name: 'addition', + kind: NodeKind.Test, + startLine: 3, + startColumn: 2, + endColumn: 3, + endLine: 5, + children: [], + }, + { + name: 'subtraction', + kind: NodeKind.Test, + startLine: 7, + startColumn: 2, + endColumn: 3, + endLine: 9, + children: [], + }, + ], + }, + ], + }, + ]); + + await expectTestTree(c, [['hello.test.ts', [['math', [['addition'], ['subtraction']]]]]]); + }); + + it('runs tests', async () => { + const c = await getController(); + const profiles = c.profiles; + expect(profiles).to.have.lengthOf(2); + + const run = await captureTestRun( + c, + new vscode.TestRunRequest( + undefined, + undefined, + profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), + ), + ); + + run.expectStates({ + 'hello.test.ts/math/addition': ['enqueued', 'started', 'passed'], + 'hello.test.ts/math/subtraction': ['enqueued', 'started', 'passed'], + }); + }); + + it('debugs tests', async () => { + const c = await getController(); + const profiles = c.profiles; + expect(profiles).to.have.lengthOf(2); + + const run = await captureTestRun( + c, + new vscode.TestRunRequest( + undefined, + undefined, + profiles.find((p) => p.kind === vscode.TestRunProfileKind.Debug), + ), + ); + + run.expectStates({ + 'hello.test.ts/math/addition': ['enqueued', 'started', 'passed'], + 'hello.test.ts/math/subtraction': ['enqueued', 'started', 'passed'], + }); + }); +}); diff --git a/src/test/integration/typescript.test.ts b/src/test/integration/typescript.test.ts new file mode 100644 index 0000000..58810fd --- /dev/null +++ b/src/test/integration/typescript.test.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import * as vscode from 'vscode'; +import { captureTestRun, expectTestTree, getController, integrationTestPrepare } from '../util'; + +describe('typescript', () => { + it('discovers tests', async () => { + const c = await getController(); + + await expectTestTree(c, [['hello.test.ts', [['math', [['addition'], ['subtraction']]]]]]); + }); + + it('runs tests', async () => { + const c = await getController(); + const profiles = c.profiles; + expect(profiles).to.have.lengthOf(2); + + const run = await captureTestRun( + c, + new vscode.TestRunRequest( + undefined, + undefined, + profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), + ), + ); + + run.expectStates({ + 'hello.test.ts/math/addition': ['enqueued', 'started', 'passed'], + 'hello.test.ts/math/subtraction': ['enqueued', 'started', 'passed'], + }); + }); + + it('debugs tests', async () => { + const c = await getController(); + const profiles = c.profiles; + expect(profiles).to.have.lengthOf(2); + + const run = await captureTestRun( + c, + new vscode.TestRunRequest( + undefined, + undefined, + profiles.find((p) => p.kind === vscode.TestRunProfileKind.Debug), + ), + ); + + run.expectStates({ + 'hello.test.ts/math/addition': ['enqueued', 'started', 'passed'], + 'hello.test.ts/math/subtraction': ['enqueued', 'started', 'passed'], + }); + }); +}); diff --git a/src/test/testCases/simple.ts b/src/test/testCases/simple.ts deleted file mode 100644 index 71f5e72..0000000 --- a/src/test/testCases/simple.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { expect } from 'chai'; -import { promises as fs } from 'fs'; -import * as path from 'path'; -import { setTimeout } from 'timers/promises'; -import * as vscode from 'vscode'; -import { - captureTestRun, - expectTestTree, - getController, - onceChanged, - saveAndRestoreWorkspace, -} from '../util'; - -const folder = path.resolve(__dirname, '../../../testCases/simple'); - -it('discovers tests', async () => { - const c = await getController(); - - await expectTestTree(c, [ - ['folder', [['nested.test.js', [['is nested']]]]], - ['goodbye.test.js', [['math', [['division']]]]], - ['hello.test.js', [['math', [['addition'], ['subtraction']]]]], - ]); -}); - -it('handles file delete', () => - saveAndRestoreWorkspace(folder, async () => { - const c = await getController(); - const onChange = onceChanged(c); - - await fs.rm(path.join(folder, 'hello.test.js')); - await onChange; - - await expectTestTree(c, [ - ['folder', [['nested.test.js', [['is nested']]]]], - ['goodbye.test.js', [['math', [['division']]]]], - ]); - })); - -it('cleans up folder if all child files are deleted', () => - saveAndRestoreWorkspace(folder, async () => { - const c = await getController(); - const onChange = onceChanged(c); - - await fs.rm(path.join(folder, 'folder/nested.test.js')); - await onChange; - - await expectTestTree(c, [ - ['goodbye.test.js', [['math', [['division']]]]], - ['hello.test.js', [['math', [['addition'], ['subtraction']]]]], - ]); - })); - -it('handles file change', () => - saveAndRestoreWorkspace(folder, async () => { - const c = await getController(); - const onChange = onceChanged(c); - - await fs.writeFile( - path.join(folder, 'hello.test.js'), - ` - test("subtraction", () => { - strictEqual(1 - 2, -1); - }); - `, - ); - await onChange; - - await expectTestTree(c, [ - ['folder', [['nested.test.js', [['is nested']]]]], - ['goodbye.test.js', [['math', [['division']]]]], - ['hello.test.js', [['subtraction']]], - ]); - })); - -it('runs tests', async () => { - const c = await getController(); - const profiles = c.profiles; - expect(profiles).to.have.lengthOf(2); - - const run = await captureTestRun( - c, - new vscode.TestRunRequest( - undefined, - undefined, - profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), - ), - ); - - run.expectStates({ - 'goodbye.test.js/math/division': ['enqueued', 'started', 'passed'], - 'hello.test.js/math/addition': ['enqueued', 'started', 'passed'], - 'hello.test.js/math/subtraction': ['enqueued', 'started', 'passed'], - 'folder/nested.test.js/is nested': ['enqueued', 'started', 'passed'], - }); -}); - -it('runs tests in directory', async () => { - const c = await getController(); - const run = await captureTestRun( - c, - new vscode.TestRunRequest( - [c.ctrl.items.get('folder')!], - undefined, - c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), - ), - ); - - run.expectStates({ - 'folder/nested.test.js/is nested': ['enqueued', 'started', 'passed'], - }); -}); - -it('runs tests in a file', async () => { - const c = await getController(); - const run = await captureTestRun( - c, - new vscode.TestRunRequest( - [c.ctrl.items.get('hello.test.js')!], - undefined, - c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), - ), - ); - - run.expectStates({ - 'hello.test.js/math/addition': ['enqueued', 'started', 'passed'], - 'hello.test.js/math/subtraction': ['enqueued', 'started', 'passed'], - }); -}); - -it('debugs tests in a file', async () => { - const c = await getController(); - const run = await captureTestRun( - c, - new vscode.TestRunRequest( - [c.ctrl.items.get('hello.test.js')!], - undefined, - c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Debug), - ), - ); - - run.expectStates({ - 'hello.test.js/math/addition': ['enqueued', 'started', 'passed'], - 'hello.test.js/math/subtraction': ['enqueued', 'started', 'passed'], - }); -}); - -it('runs subsets of tests', async () => { - const c = await getController(); - const run = await captureTestRun( - c, - new vscode.TestRunRequest( - [c.ctrl.items.get('hello.test.js')!.children.get('math')!.children.get('addition')!], - undefined, - c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), - ), - ); - - run.expectStates({ - 'hello.test.js/math/addition': ['enqueued', 'started', 'passed'], - }); -}); - -it('handles file and directory excludes', async () => { - const c = await getController(); - const run = await captureTestRun( - c, - new vscode.TestRunRequest( - undefined, - [c.ctrl.items.get('hello.test.js')!, c.ctrl.items.get('folder')!], - c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), - ), - ); - - run.expectStates({ - 'goodbye.test.js/math/division': ['enqueued', 'started', 'passed'], - }); -}); - -it('handles changes to .vscode-test.js', () => - saveAndRestoreWorkspace(folder, async () => { - const c = await getController(); - const onChange = onceChanged(c); - - const configPath = path.join(folder, '.vscode-test.js'); - const original = await fs.readFile(configPath, 'utf-8'); - let updated = original.replace('**/*.test.js', '*.test.js'); - - // the vscode file watcher is set up async and does not always catch the change, keep changing the file - while (true) { - updated += '\n//'; - await fs.writeFile(configPath, updated); - const ok = await Promise.race([onChange.then(() => true), setTimeout(500)]); - if (ok) { - break; - } - } - - await expectTestTree(c, [ - ['goodbye.test.js', [['math', [['division']]]]], - ['hello.test.js', [['math', [['addition'], ['subtraction']]]]], - ]); - })); diff --git a/src/test/testCases/sourceMapped.ts b/src/test/testCases/sourceMapped.ts deleted file mode 100644 index 7cdd9db..0000000 --- a/src/test/testCases/sourceMapped.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { expect } from 'chai'; -import { promises as fs } from 'fs'; -import * as path from 'path'; -import { setTimeout } from 'timers/promises'; -import * as vscode from 'vscode'; -import { - captureTestRun, - expectTestTree, - getController, - onceChanged, - saveAndRestoreWorkspace, -} from '../util'; - -const folder = path.resolve(__dirname, '../../../testCases/sourceMapped'); - -it('discovers tests', async () => { - const c = await getController(); - - await expectTestTree(c, [ - ['folder', [['nested.test.ts', [['is nested']]]]], - ['goodbye.test.ts', [['math', [['division']]]]], - ['hello.test.ts', [['math', [['addition'], ['subtraction']]]]], - ]); -}); - -it('handles file delete', () => - saveAndRestoreWorkspace(folder, async () => { - const c = await getController(); - const onChange = onceChanged(c); - - await fs.rm(path.join(folder, 'hello.test.js')); - await onChange; - - await expectTestTree(c, [ - ['folder', [['nested.test.ts', [['is nested']]]]], - ['goodbye.test.ts', [['math', [['division']]]]], - ]); - })); - -it('cleans up folder if all child files are deleted', () => - saveAndRestoreWorkspace(folder, async () => { - const c = await getController(); - const onChange = onceChanged(c); - - await fs.rm(path.join(folder, 'folder/nested.test.js')); - await onChange; - - await expectTestTree(c, [ - ['goodbye.test.ts', [['math', [['division']]]]], - ['hello.test.ts', [['math', [['addition'], ['subtraction']]]]], - ]); - })); - -it('runs tests', async () => { - const c = await getController(); - const profiles = c.profiles; - expect(profiles).to.have.lengthOf(2); - - const run = await captureTestRun( - c, - new vscode.TestRunRequest( - undefined, - undefined, - profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), - ), - ); - - run.expectStates({ - 'goodbye.test.ts/math/division': ['enqueued', 'started', 'passed'], - 'hello.test.ts/math/addition': ['enqueued', 'started', 'passed'], - 'hello.test.ts/math/subtraction': ['enqueued', 'started', 'passed'], - 'folder/nested.test.ts/is nested': ['enqueued', 'started', 'passed'], - }); -}); - -it('runs tests in directory', async () => { - const c = await getController(); - const run = await captureTestRun( - c, - new vscode.TestRunRequest( - [c.ctrl.items.get('folder')!], - undefined, - c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), - ), - ); - - run.expectStates({ - 'folder/nested.test.ts/is nested': ['enqueued', 'started', 'passed'], - }); -}); - -it('runs tests in a file', async () => { - const c = await getController(); - const run = await captureTestRun( - c, - new vscode.TestRunRequest( - [c.ctrl.items.get('hello.test.ts')!], - undefined, - c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), - ), - ); - - run.expectStates({ - 'hello.test.ts/math/addition': ['enqueued', 'started', 'passed'], - 'hello.test.ts/math/subtraction': ['enqueued', 'started', 'passed'], - }); -}); - -it('runs subsets of tests', async () => { - const c = await getController(); - const run = await captureTestRun( - c, - new vscode.TestRunRequest( - [c.ctrl.items.get('hello.test.ts')!.children.get('math')!.children.get('addition')!], - undefined, - c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), - ), - ); - - run.expectStates({ - 'hello.test.ts/math/addition': ['enqueued', 'started', 'passed'], - }); -}); - -it('handles file and directory excludes', async () => { - const c = await getController(); - const run = await captureTestRun( - c, - new vscode.TestRunRequest( - undefined, - [c.ctrl.items.get('hello.test.ts')!, c.ctrl.items.get('folder')!], - c.profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run), - ), - ); - - run.expectStates({ - 'goodbye.test.ts/math/division': ['enqueued', 'started', 'passed'], - }); -}); - -it('handles changes to .vscode-test.js', () => - saveAndRestoreWorkspace(folder, async () => { - const c = await getController(); - const onChange = onceChanged(c); - - const configPath = path.join(folder, '.vscode-test.js'); - const original = await fs.readFile(configPath, 'utf-8'); - let updated = original.replace('**/*.test.js', '*.test.js'); - - // the vscode file watcher is set up async and does not always catch the change, keep changing the file - while (true) { - updated += '\n//'; - await fs.writeFile(configPath, updated); - const ok = await Promise.race([onChange.then(() => true), setTimeout(500)]); - if (ok) { - break; - } - } - - await expectTestTree(c, [ - ['goodbye.test.ts', [['math', [['division']]]]], - ['hello.test.ts', [['math', [['addition'], ['subtraction']]]]], - ]); - })); diff --git a/src/test/unit/extract/evaluate-typescript.test.ts b/src/test/unit/extract/evaluate-typescript.test.ts new file mode 100644 index 0000000..24df63d --- /dev/null +++ b/src/test/unit/extract/evaluate-typescript.test.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { defaultTestSymbols } from '../../../constants'; +import { NodeKind } from '../../../extract'; +import { extractWithEvaluation } from '../../../extract/evaluate'; +import { source } from '../../util'; + +describe('evaluate typescript', () => { + it('extracts basic suite', async () => { + const src = await extractWithEvaluation( + undefined, + 'test.ts', + source( + "suite('hello', () => {", // + " it('works', () => {});", + '})', + ), + defaultTestSymbols, + ); + expect(src).to.deep.equal([ + { + name: 'hello', + kind: NodeKind.Suite, + startLine: 0, + startColumn: 0, + endColumn: 1, + endLine: 2, + children: [ + { + name: 'works', + kind: NodeKind.Test, + startLine: 1, + startColumn: 2, + endColumn: Number.MAX_SAFE_INTEGER, + endLine: 1, + children: [], + }, + ], + }, + ]); + }); + + it('extracts basic suite ts syntax', async () => { + const src = await extractWithEvaluation( + undefined, + 'test.ts', + source( + 'function topLevel(a: number): string {', // + ' return a.toString() as string;', + '}', + '', + "suite('hello', () => {", + ' function inDescribe(a: number): string {', + ' return a.toString() as string;', + ' }', + " it('works', () => {});", + '})', + ), + defaultTestSymbols, + ); + expect(src).to.deep.equal([ + { + name: 'hello', + kind: NodeKind.Suite, + startLine: 4, + startColumn: 0, + endColumn: 1, + endLine: 9, + children: [ + { + name: 'works', + kind: NodeKind.Test, + startLine: 8, + startColumn: 2, + endColumn: Number.MAX_SAFE_INTEGER, + endLine: 8, + children: [], + }, + ], + }, + ]); + }); + + it('extracts multiple suite', async () => { + const src = await extractWithEvaluation( + undefined, + 'test.ts', + source( + "suite('hello', () => {", // + " it('works', () => {});", + '', + '', + " it('works2', () => {});", + '})', + '', + ' ', + '// ', + "suite('hello2', () => {", + " it('works', () => {});", + '', + '', + " it('works2', () => {});", + '})', + ), + defaultTestSymbols, + ); + expect(src).to.deep.equal([ + { + name: 'hello', + kind: NodeKind.Suite, + startLine: 0, + startColumn: 0, + endColumn: 1, + endLine: 5, + children: [ + { + name: 'works', + kind: NodeKind.Test, + startLine: 1, + startColumn: 2, + endColumn: Number.MAX_SAFE_INTEGER, + endLine: 1, + children: [], + }, + { + name: 'works2', + kind: NodeKind.Test, + startLine: 4, + startColumn: 2, + endColumn: Number.MAX_SAFE_INTEGER, + endLine: 4, + children: [], + }, + ], + }, + { + name: 'hello2', + kind: NodeKind.Suite, + startLine: 9, + startColumn: 0, + endColumn: 1, + endLine: 14, + children: [ + { + name: 'works', + kind: NodeKind.Test, + startLine: 10, + startColumn: 2, + endColumn: Number.MAX_SAFE_INTEGER, + endLine: 10, + children: [], + }, + { + name: 'works2', + kind: NodeKind.Test, + startLine: 13, + startColumn: 2, + endColumn: Number.MAX_SAFE_INTEGER, + endLine: 13, + children: [], + }, + ], + }, + ]); + }); +}); diff --git a/src/extract/evaluate.test.ts b/src/test/unit/extract/evaluate.test.ts similarity index 51% rename from src/extract/evaluate.test.ts rename to src/test/unit/extract/evaluate.test.ts index 87d4a90..9b9ccc9 100644 --- a/src/extract/evaluate.test.ts +++ b/src/test/unit/extract/evaluate.test.ts @@ -1,36 +1,42 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ import { expect } from 'chai'; -import { NodeKind } from '.'; -import { defaultTestSymbols } from '../constants'; -import { extractWithEvaluation } from './evaluate'; +import { defaultTestSymbols } from '../../../constants'; +import { NodeKind } from '../../../extract'; +import { extractWithEvaluation } from '../../../extract/evaluate'; +import { source } from '../../util'; describe('evaluate', () => { - it('extracts basic suite', () => { - const src = extractWithEvaluation( - `suite('hello', () => { - it('works', () => {}); - })`, + it('extracts basic suite', async () => { + const src = await extractWithEvaluation( + undefined, + 'test.js', + source( + "suite('hello', () => {", // + " it('works', () => {});", + '})', + ), defaultTestSymbols, ); expect(src).to.deep.equal([ { name: 'hello', kind: NodeKind.Suite, - startLine: 1, - startColumn: 1, - endColumn: 5, - endLine: 3, + startLine: 0, + startColumn: 0, + endColumn: 1, + endLine: 2, children: [ { name: 'works', kind: NodeKind.Test, - startLine: 2, - startColumn: 7, + startLine: 1, + startColumn: 2, endColumn: Number.MAX_SAFE_INTEGER, - endLine: 2, + endLine: 1, children: [], }, ], @@ -38,48 +44,52 @@ describe('evaluate', () => { ]); }); - it('can evaluate and extract a test table', () => { - const src = extractWithEvaluation( - `suite('hello', () => { - for (const name of ['foo', 'bar', 'baz']) { - it(name, () => {}); - } - })`, + it('can evaluate and extract a test table', async () => { + const src = await extractWithEvaluation( + undefined, + 'test.js', + source( + "suite('hello', () => {", // + " for (const name of ['foo', 'bar', 'baz']) {", + ' it(name, () => {});', + ' }', + '})', + ), defaultTestSymbols, ); expect(src).to.deep.equal([ { name: 'hello', kind: NodeKind.Suite, - startLine: 1, - startColumn: 1, - endColumn: 5, - endLine: 5, + startLine: 0, + startColumn: 0, + endColumn: 1, + endLine: 4, children: [ { name: 'foo', kind: NodeKind.Test, - startLine: 3, - startColumn: 9, - endLine: 3, + startLine: 2, + startColumn: 4, + endLine: 2, endColumn: Number.MAX_SAFE_INTEGER, children: [], }, { name: 'bar', kind: NodeKind.Test, - startLine: 3, - startColumn: 9, - endLine: 3, + startLine: 2, + startColumn: 4, + endLine: 2, endColumn: Number.MAX_SAFE_INTEGER, children: [], }, { name: 'baz', kind: NodeKind.Test, - startLine: 3, - startColumn: 9, - endLine: 3, + startLine: 2, + startColumn: 4, + endLine: 2, endColumn: Number.MAX_SAFE_INTEGER, children: [], }, @@ -87,49 +97,57 @@ describe('evaluate', () => { }, ]); }); - it('handles errors appropriately', () => { - const src = extractWithEvaluation( - `suite('hello', () => { - throw new Error('whoops'); - })`, + it('handles errors appropriately', async () => { + const src = await extractWithEvaluation( + undefined, + 'test.js', + source( + "suite('hello', () => {", // + " throw new Error('whoops');", + '})', + ), defaultTestSymbols, ); expect(src).to.deep.equal([ { name: 'hello', kind: NodeKind.Suite, - startLine: 1, - startColumn: 1, - endLine: 3, - endColumn: 5, + startLine: 0, + startColumn: 0, + endLine: 2, + endColumn: 1, children: [], error: 'whoops', }, ]); }); - it('works with skip/only', () => { - const src = extractWithEvaluation( - `suite('hello', () => { - it.only('a', ()=>{}); - it.skip('a', ()=>{}); - })`, + it('works with skip/only', async () => { + const src = await extractWithEvaluation( + undefined, + 'test.js', + source( + "suite('hello', () => {", // + " it.only('a', ()=>{});", + " it.skip('a', ()=>{});", + '})', + ), defaultTestSymbols, ); expect(src).to.deep.equal([ { name: 'hello', kind: NodeKind.Suite, - startLine: 1, - startColumn: 1, - endLine: 4, - endColumn: 5, + startLine: 0, + startColumn: 0, + endLine: 3, + endColumn: 1, children: [ { name: 'a', kind: NodeKind.Test, - startLine: 2, - startColumn: 12, - endLine: 2, + startLine: 1, + startColumn: 5, // marked at the begin of only() + endLine: 1, endColumn: Number.MAX_SAFE_INTEGER, children: [], directive: 'only', @@ -137,9 +155,9 @@ describe('evaluate', () => { { name: 'a', kind: NodeKind.Test, - startLine: 3, - startColumn: 12, - endLine: 3, + startLine: 2, + startColumn: 5, + endLine: 2, endColumn: Number.MAX_SAFE_INTEGER, children: [], directive: 'skip', @@ -149,16 +167,20 @@ describe('evaluate', () => { ]); }); - it('stubs out requires and placeholds correctly', () => { - const src = extractWithEvaluation( + it('stubs out requires and placeholds correctly', async () => { + const src = await extractWithEvaluation( + undefined, + 'test.js', `require("some invalid module").doing().other.things()`, defaultTestSymbols, ); expect(src).to.deep.equal([]); }); - it('runs esbuild-style modules', () => { - const src = extractWithEvaluation( + it('runs esbuild-style modules', async () => { + const src = await extractWithEvaluation( + undefined, + 'test.js', `var foo = () => suite('hello', () => {}); foo();`, defaultTestSymbols, ); @@ -166,9 +188,9 @@ describe('evaluate', () => { { name: 'hello', kind: 0, - startLine: 1, - startColumn: 17, - endLine: 1, + startLine: 0, + startColumn: 16, + endLine: 0, endColumn: Number.MAX_SAFE_INTEGER, children: [], }, diff --git a/src/test/unit/extract/syntax.test.ts b/src/test/unit/extract/syntax.test.ts new file mode 100644 index 0000000..2a9bd70 --- /dev/null +++ b/src/test/unit/extract/syntax.test.ts @@ -0,0 +1,124 @@ +/*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { defaultTestSymbols } from '../../../constants'; +import { NodeKind } from '../../../extract'; +import { extractWithAst } from '../../../extract/syntax'; +import { source } from '../../util'; + +describe('syntax', () => { + it('extracts basic suite', () => { + const src = extractWithAst( + 'test.js', + source( + "suite('hello', () => {", // + " it('works', () => {});", + '})', + ), + defaultTestSymbols, + ); + expect(src).to.deep.equal([ + { + name: 'hello', + startLine: 0, + kind: NodeKind.Suite, + startColumn: 0, + endColumn: 2, + endLine: 2, + children: [ + { + name: 'works', + kind: NodeKind.Test, + startLine: 1, + startColumn: 2, + endColumn: 23, + endLine: 1, + children: [], + }, + ], + }, + ]); + }); + + it('works with skip/only', () => { + const src = extractWithAst( + 'test.js', + source( + "suite('hello', () => {", // + " it.only('a', ()=>{});", + " it.skip('a', ()=>{});", + '})', + ), + defaultTestSymbols, + ); + expect(src).to.deep.equal([ + { + name: 'hello', + kind: NodeKind.Suite, + startLine: 0, + startColumn: 0, + endColumn: 2, + endLine: 3, + children: [ + { + name: 'a', + kind: NodeKind.Test, + startLine: 1, + startColumn: 2, + endColumn: 22, + endLine: 1, + children: [], + directive: 'only', + }, + { + name: 'a', + kind: NodeKind.Test, + startLine: 2, + startColumn: 2, + endColumn: 22, + endLine: 2, + children: [], + directive: 'skip', + }, + ], + }, + ]); + }); + + it('can detect suite but not dynamic tests', () => { + const src = extractWithAst( + 'test.js', + source( + "suite('hello', () => {", // + " for (const name of ['foo', 'bar', 'baz']) {", + ' it(name, () => {});', + ' }', + '})', + ), + defaultTestSymbols, + ); + expect(src).to.deep.equal([ + { + name: 'hello', + kind: NodeKind.Suite, + startLine: 0, + startColumn: 0, + endColumn: 2, + endLine: 4, + children: [], + }, + ]); + }); + + it('stubs out requires and placeholds correctly', () => { + const src = extractWithAst( + 'test.js', + `require("some invalid module").doing().other.things()`, + defaultTestSymbols, + ); + expect(src).to.deep.equal([]); + }); +}); diff --git a/src/test/util.ts b/src/test/util.ts index 54b0367..1c5d96e 100644 --- a/src/test/util.ts +++ b/src/test/util.ts @@ -1,10 +1,11 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ import * as assert from 'assert'; -import { randomBytes } from 'crypto'; -import { promises as fs } from 'fs'; +import * as crypto from 'crypto'; +import * as fs from 'fs'; import { tmpdir } from 'os'; import * as path from 'path'; import * as sinon from 'sinon'; @@ -12,8 +13,55 @@ import { setTimeout } from 'timers/promises'; import * as vscode from 'vscode'; import { getControllersForTestCommand } from '../constants'; import type { Controller } from '../controller'; +import { IParsedNode, NodeKind } from '../extract'; -export const getController = async () => { +export function source(...lines: string[]) { + return lines.join('\n'); +} + +export function integrationTestPrepare(name: string) { + let workspaceBackup: string; + + const workspaceFolder = path.resolve(__dirname, '..', '..', 'test-workspaces', name); + if (!fs.existsSync(workspaceFolder)) { + assert.fail( + `Workspace Folder '${workspaceFolder}' doesn't exist, something is wrong with the test setup`, + ); + } + + beforeEach(async () => { + workspaceBackup = await backupWorkspace(workspaceFolder); + }); + + afterEach(async () => { + await restoreWorkspace(workspaceFolder, workspaceBackup); + }); + + return workspaceFolder; +} + +async function restoreWorkspace(workspaceFolder: string, workspaceBackup: string) { + // vscode behaves badly when we delete the workspace folder; delete contents instead. + const files = await fs.promises.readdir(workspaceFolder); + await Promise.all(files.map((f) => rmrf(path.join(workspaceFolder, f)))); + + await fs.promises.cp(workspaceBackup, workspaceFolder, { recursive: true }); + await rmrf(workspaceBackup); + + // it seems like all these files changes can require a moment for vscode's file + // watcher to update before we can run the next test. 500 seems to do it 🤷‍♂️ + await setTimeout(500); +} + +async function backupWorkspace(source: string) { + const backupFolder = path.resolve(tmpdir(), '.mocha-vscode-test-backup', crypto.randomUUID()); + await rmrf(backupFolder); + await fs.promises.cp(source, backupFolder, { recursive: true }); + + return backupFolder; +} + +export async function getController() { const c = await vscode.commands.executeCommand(getControllersForTestCommand); if (!c.length) { @@ -23,11 +71,36 @@ export const getController = async () => { const controller = c[0]; await controller.scanFiles(); return controller; -}; +} + +export function extractParsedNodes(vsItems: vscode.TestItemCollection): IParsedNode[] { + const items: IParsedNode[] = []; + + for (const vsItem of vsItems) { + const hasChildren = vsItem[1].children.size > 0; + + const item: IParsedNode = { + name: vsItem[1].label, + kind: hasChildren ? NodeKind.Suite : NodeKind.Test, + startLine: vsItem[1].range?.start.line ?? -1, + startColumn: vsItem[1].range?.start.character ?? -1, + endLine: vsItem[1].range?.end.line ?? -1, + endColumn: vsItem[1].range?.end.character ?? -1, + children: [], + }; + + items.push(item); + if (hasChildren) { + item.children = extractParsedNodes(vsItem[1].children); + } + } + + return items; +} type TestTreeExpectation = [string, TestTreeExpectation[]?]; -const buildTreeExpectation = (entry: TestTreeExpectation, c: vscode.TestItemCollection) => { +function buildTreeExpectation(entry: TestTreeExpectation, c: vscode.TestItemCollection) { for (const [id, { children }] of c) { const node: TestTreeExpectation = [id]; buildTreeExpectation(node, children); @@ -39,27 +112,29 @@ const buildTreeExpectation = (entry: TestTreeExpectation, c: vscode.TestItemColl } entry[1]?.sort(([a], [b]) => a.localeCompare(b)); -}; +} -export const onceChanged = (controller: Controller) => - new Promise((resolve) => { +export function onceChanged(controller: Controller, timeout: number = 10000) { + return new Promise((resolve, reject) => { + setTimeout(timeout).then(reject); const l = controller.onDidChange(() => { l.dispose(); resolve(); }); }); +} -export const expectTestTree = async ({ ctrl }: Controller, tree: TestTreeExpectation[]) => { +export async function expectTestTree({ ctrl }: Controller, tree: TestTreeExpectation[]) { const e = ['root', []] satisfies TestTreeExpectation; buildTreeExpectation(e, ctrl.items); assert.deepStrictEqual(e[1], tree, JSON.stringify(e[1])); -}; +} /** Retries deletion a few times since directories may still be in use briefly during test shutdown */ -const rmrf = async (path: string) => { +async function rmrf(path: string) { for (let i = 10; i >= 0; i--) { try { - await fs.rm(path, { recursive: true, force: true }); + await fs.promises.rm(path, { recursive: true, force: true }); return; } catch (e) { if (i === 0) { @@ -67,28 +142,7 @@ const rmrf = async (path: string) => { } } } -}; - -export const saveAndRestoreWorkspace = async (original: string, fn: () => unknown) => { - const backup = path.join(tmpdir(), `ext-test-backup-${randomBytes(8).toString('hex')}`); - await rmrf(path.join(original, '.vscode-test')); - await fs.cp(original, backup, { recursive: true }); - - try { - await fn(); - } finally { - // vscode behaves badly when we delete the workspace folder; delete contents instead. - const files = await fs.readdir(original); - await Promise.all(files.map((f) => rmrf(path.join(original, f)))); - - await fs.cp(backup, original, { recursive: true }); - await rmrf(backup); - - // it seems like all these files changes can require a moment for vscode's file - // watcher to update before we can run the next test. 500 seems to do it 🤷‍♂️ - await setTimeout(500); - } -}; +} type TestState = 'enqueued' | 'started' | 'skipped' | 'failed' | 'errored' | 'passed'; @@ -96,6 +150,7 @@ export class FakeTestRun implements vscode.TestRun { public output: { output: string; location?: vscode.Location; test?: vscode.TestItem }[] = []; public states: { test: vscode.TestItem; state: TestState; message?: vscode.TestMessage }[] = []; public ended = false; + public coverage: vscode.FileCoverage[] = []; public terminalStates() { const last: typeof this.states = []; @@ -172,10 +227,14 @@ export class FakeTestRun implements vscode.TestRun { end(): void { this.ended = true; } + addCoverage(fileCoverage: vscode.FileCoverage): void { + this.coverage.push(fileCoverage); + } + onDidDispose: vscode.Event = new vscode.EventEmitter().event; //#endregion } -export const captureTestRun = async (ctrl: Controller, req: vscode.TestRunRequest) => { +export async function captureTestRun(ctrl: Controller, req: vscode.TestRunRequest) { const fake = new FakeTestRun(); const createTestRun = sinon.stub(ctrl.ctrl, 'createTestRun').returns(fake); try { @@ -184,4 +243,4 @@ export const captureTestRun = async (ctrl: Controller, req: vscode.TestRunReques } finally { createTestRun.restore(); } -}; +} diff --git a/src/test/workspace-runner.ts b/src/test/workspace-runner.ts deleted file mode 100644 index c3b21b2..0000000 --- a/src/test/workspace-runner.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as inspector from "inspector"; -import Mocha from "mocha"; -import * as path from "path"; -import vscode from "vscode"; - -export function run(): Promise { - const mocha = new Mocha({ - ui: "bdd", - color: true, - bail: true, - timeout: inspector.url() ? Infinity : 5000, - }); - - const workspace = vscode.workspace.workspaceFolders?.[0]; - if (!workspace) { - throw new Error("expected to have open workspace folder"); - } - - return new Promise((c, e) => { - try { - mocha.addFile(path.resolve(workspace.uri.fsPath, "../runner.js")); - mocha.run((failures: number) => { - if (failures > 0) { - e(new Error(`${failures} tests failed.`)); - } else { - c(); - } - }); - } catch (err) { - console.error(err); - e(err); - } - }); -} diff --git a/src/typings/acorn-loose.d.ts b/src/typings/acorn-loose.d.ts index 4603965..c4a8c10 100644 --- a/src/typings/acorn-loose.d.ts +++ b/src/typings/acorn-loose.d.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/test-workspaces/simple/.mocharc.js b/test-workspaces/simple/.mocharc.js new file mode 100644 index 0000000..9df0b71 --- /dev/null +++ b/test-workspaces/simple/.mocharc.js @@ -0,0 +1,3 @@ +module.exports = { + spec: '**/*.test.js' +}; \ No newline at end of file diff --git a/testCases/simple/folder/nested.test.js b/test-workspaces/simple/folder/nested.test.js similarity index 100% rename from testCases/simple/folder/nested.test.js rename to test-workspaces/simple/folder/nested.test.js diff --git a/testCases/simple/goodbye.test.js b/test-workspaces/simple/goodbye.test.js similarity index 100% rename from testCases/simple/goodbye.test.js rename to test-workspaces/simple/goodbye.test.js diff --git a/testCases/simple/hello.test.js b/test-workspaces/simple/hello.test.js similarity index 100% rename from testCases/simple/hello.test.js rename to test-workspaces/simple/hello.test.js diff --git a/test-workspaces/source-mapped/.mocharc.js b/test-workspaces/source-mapped/.mocharc.js new file mode 100644 index 0000000..9df0b71 --- /dev/null +++ b/test-workspaces/source-mapped/.mocharc.js @@ -0,0 +1,3 @@ +module.exports = { + spec: '**/*.test.js' +}; \ No newline at end of file diff --git a/testCases/sourceMapped/hello.test.js b/test-workspaces/source-mapped/hello.test.js similarity index 100% rename from testCases/sourceMapped/hello.test.js rename to test-workspaces/source-mapped/hello.test.js diff --git a/testCases/sourceMapped/hello.test.js.map b/test-workspaces/source-mapped/hello.test.js.map similarity index 100% rename from testCases/sourceMapped/hello.test.js.map rename to test-workspaces/source-mapped/hello.test.js.map diff --git a/testCases/sourceMapped/hello.test.ts b/test-workspaces/source-mapped/hello.test.ts similarity index 100% rename from testCases/sourceMapped/hello.test.ts rename to test-workspaces/source-mapped/hello.test.ts diff --git a/testCases/sourceMapped/package.json b/test-workspaces/source-mapped/package.json similarity index 100% rename from testCases/sourceMapped/package.json rename to test-workspaces/source-mapped/package.json diff --git a/testCases/sourceMapped/tsconfig.json b/test-workspaces/source-mapped/tsconfig.json similarity index 100% rename from testCases/sourceMapped/tsconfig.json rename to test-workspaces/source-mapped/tsconfig.json diff --git a/test-workspaces/typescript/.mocharc.js b/test-workspaces/typescript/.mocharc.js new file mode 100644 index 0000000..27be97f --- /dev/null +++ b/test-workspaces/typescript/.mocharc.js @@ -0,0 +1,11 @@ +module.exports = { + spec: '**/*.test.ts', + extension: [ + "ts" + ], + "node-option": [ + "experimental-specifier-resolution=node", + "import=tsx", + "no-warnings" + ], +}; \ No newline at end of file diff --git a/test-workspaces/typescript/hello.test.ts b/test-workspaces/typescript/hello.test.ts new file mode 100644 index 0000000..b570699 --- /dev/null +++ b/test-workspaces/typescript/hello.test.ts @@ -0,0 +1,21 @@ +const { strictEqual } = require('node:assert'); + +// just some typescript code which would be valid directly in Node + +function topLevel(a: number): string { + return a.toString() as string; +} + +describe('math', () => { + function inDescribe(a: number): string { + return a.toString() as string; + } + + it('addition', async () => { + strictEqual(1 + 1 as number, 2 as any as number); + }); + + it(`subtraction`, async () => { + strictEqual(1 - 1 as number, 0 as any as number); + }); +}); diff --git a/testCases/simple/.vscode-test.js b/testCases/simple/.vscode-test.js deleted file mode 100644 index 7c50074..0000000 --- a/testCases/simple/.vscode-test.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - files: '**/*.test.js', - cachePath: `${__dirname}/../../.vscode-test`, - mocha: { ui: 'bdd' }, -}; - diff --git a/testCases/sourceMapped/.vscode-test.js b/testCases/sourceMapped/.vscode-test.js deleted file mode 100644 index 7c50074..0000000 --- a/testCases/sourceMapped/.vscode-test.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - files: '**/*.test.js', - cachePath: `${__dirname}/../../.vscode-test`, - mocha: { ui: 'bdd' }, -}; - diff --git a/testCases/sourceMapped/folder/nested.test.js b/testCases/sourceMapped/folder/nested.test.js deleted file mode 100644 index c9b0f0b..0000000 --- a/testCases/sourceMapped/folder/nested.test.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -it('is nested', async () => { }); -//# sourceMappingURL=nested.test.js.map \ No newline at end of file diff --git a/testCases/sourceMapped/folder/nested.test.js.map b/testCases/sourceMapped/folder/nested.test.js.map deleted file mode 100644 index 27f2add..0000000 --- a/testCases/sourceMapped/folder/nested.test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"nested.test.js","sourceRoot":"","sources":["nested.test.ts"],"names":[],"mappings":";AAAA,EAAE,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/testCases/sourceMapped/folder/nested.test.ts b/testCases/sourceMapped/folder/nested.test.ts deleted file mode 100644 index 4bca548..0000000 --- a/testCases/sourceMapped/folder/nested.test.ts +++ /dev/null @@ -1 +0,0 @@ -it('is nested', async () => {}); diff --git a/testCases/sourceMapped/goodbye.test.js b/testCases/sourceMapped/goodbye.test.js deleted file mode 100644 index 3fad6b7..0000000 --- a/testCases/sourceMapped/goodbye.test.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const node_assert_1 = require("node:assert"); -describe('math', () => { - it('division', async () => { - (0, node_assert_1.strictEqual)(2 / 1, 2); - }); -}); -//# sourceMappingURL=goodbye.test.js.map \ No newline at end of file diff --git a/testCases/sourceMapped/goodbye.test.js.map b/testCases/sourceMapped/goodbye.test.js.map deleted file mode 100644 index d245315..0000000 --- a/testCases/sourceMapped/goodbye.test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"goodbye.test.js","sourceRoot":"","sources":["goodbye.test.ts"],"names":[],"mappings":";;AAAA,6CAA0C;AAG1C,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;IACpB,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;QACxB,IAAA,yBAAW,EAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/testCases/sourceMapped/goodbye.test.ts b/testCases/sourceMapped/goodbye.test.ts deleted file mode 100644 index 8245667..0000000 --- a/testCases/sourceMapped/goodbye.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { strictEqual } from 'node:assert'; - - -describe('math', () => { - it('division', async () => { - strictEqual(2 / 1, 2); - }); -}); diff --git a/tsconfig.json b/tsconfig.json index 6ef5af0..316a72f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,9 @@ "module": "commonjs", "target": "ES2021", "outDir": "out", - "lib": ["ES2021"], - "types": ["node"], + "lib": [ + "ES2021" + ], "sourceMap": true, "skipLibCheck": true, "strict": true /* enable all strict type-checking options */, @@ -15,5 +16,7 @@ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ }, - "include": ["src"] -} + "include": [ + "src" + ] +} \ No newline at end of file