From af988dd6f269e0ac2d944ec80cd60c435d09b49d Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 24 Mar 2024 10:18:45 +0100 Subject: [PATCH 01/26] refactor: Rebrand project for Mocha --- CODE_OF_CONDUCT.md | 9 ----- CONTRIBUTING.md | 27 +++++++++++++++ LICENSE | 1 + README.md | 46 +++++++++---------------- SUPPORT.md | 25 -------------- icon.png | Bin 9071 -> 13378 bytes package-lock.json | 8 ++--- package.json | 40 ++++++++++++++-------- pipeline.yml | 53 ----------------------------- src/configValue.ts | 5 +++ src/configurationFile.ts | 2 ++ src/constants.ts | 1 + src/controller.ts | 1 + src/coverage.ts | 1 + src/disposable.ts | 1 + src/errors.ts | 3 +- src/extension.ts | 7 +++- src/extract/evaluate.test.ts | 1 + src/extract/evaluate.ts | 5 +++ src/extract/index.ts | 1 + src/extract/syntax.test.ts | 1 + src/extract/syntax.ts | 5 +-- src/iterable.ts | 1 + src/metadata.ts | 1 + src/outputQueue.ts | 1 + src/runner.ts | 12 +++---- src/source-map-store.ts | 1 + src/source-map.ts | 1 + src/test/testCases/simple.ts | 5 +++ src/test/testCases/sourceMapped.ts | 5 +++ src/test/util.ts | 1 + src/test/workspace-runner.ts | 5 +++ src/typings/acorn-loose.d.ts | 1 + 33 files changed, 131 insertions(+), 146 deletions(-) delete mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md delete mode 100644 SUPPORT.md delete mode 100644 pipeline.yml 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..f9a80c2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ MIT License + Copyright (c) OpenJS Foundation and contributors, https://openjsf.org 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..a9990ef 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,31 @@ -# VS Code Extension Test Runner +# Mocha VSCode 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. -## 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). +## Credits -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 project started as a fork of the `Extension Test Runner` 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. -1. Modify your tsconfig.json and add `"sourceMap": true` -1. Add `**/*.js.map` to your `.vscodeignore` file to avoid bloating the published extension. +* https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner +* https://github.com/microsoft/vscode-extension-test-runner -## Configuration -- `extension-test-runner.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 `test` and `suite` identifiers the process extracts. Defaults to `["it", "test"]` and `["describe", "suite"]` respectively, covering Mocha's common interfaces. +## Getting Started -- `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. +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). -## Contributing +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: -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. +1. Modify your tsconfig.json and add `"sourceMap": true` -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. +## Configuration -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. +- `mocha-vscode.extractSettings`: configures how tests get extracted. You can configure: -## Trademarks + - 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 `test` and `suite` identifiers the process extracts. Defaults to `["it", "test"]` and `["describe", "suite"]` respectively, covering Mocha's common interfaces. -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/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 8176d40ab894329a8ea63fab9b196fb2c27068be..e78b6c6ea36f898856b4679d97672e90c5a1fb84 100644 GIT binary patch literal 13378 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 literal 9071 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 diff --git a/package-lock.json b/package-lock.json index 20338ca..98f6bac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "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", diff --git a/package.json b/package.json index 8ba075f..6842b31 100644 --- a/package.json +++ b/package.json @@ -1,17 +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": "mocha", + "version": "1.0.0", "icon": "icon.png", "engines": { "vscode": "^1.83.0" }, "keywords": [ - "vscode", + "mocha", "test", - "cli" + "bdd", + "tdd", + "tap", + "testing", + "chai", + "assertion", + "ava", + "jest", + "tape", + "jasmine", + "karma" ], "enabledApiProposals": [ "testCoverage" @@ -30,7 +40,7 @@ { "title": "Extension Test Runner", "properties": { - "extension-test-runner.extractSettings": { + "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 `test` and `suite` identifiers the process extracts.", "type": "object", "properties": { @@ -71,7 +81,7 @@ "extractWith" ] }, - "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,17 +91,17 @@ ] }, "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", @@ -142,4 +152,4 @@ "split2": "^4.2.0", "stacktrace-parser": "^0.1.10" } -} +} \ No newline at end of file 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..0ba5952 100644 --- a/src/configValue.ts +++ b/src/configValue.ts @@ -1,3 +1,8 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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..8c1c706 100644 --- a/src/configurationFile.ts +++ b/src/configurationFile.ts @@ -1,6 +1,8 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * 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'; diff --git a/src/constants.ts b/src/constants.ts index 74bad3c..315d189 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/controller.ts b/src/controller.ts index b276f2e..b614859 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/coverage.ts b/src/coverage.ts index b952d2b..9e30615 100644 --- a/src/coverage.ts +++ b/src/coverage.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/disposable.ts b/src/disposable.ts index 638a31e..e87bb3a 100644 --- a/src/disposable.ts +++ b/src/disposable.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/errors.ts b/src/errors.ts index 9b91c91..fbdcb43 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,9 +1,10 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ /** Errors with a human-readable message. */ -export class HumanError extends Error {} +export class HumanError extends Error { } export class CliPackageMissing extends HumanError { constructor(innerError: Error) { diff --git a/src/extension.ts b/src/extension.ts index 831bd21..3555f30 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,3 +1,8 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + import * as path from 'path'; import * as timers from 'timers/promises'; import * as vscode from 'vscode'; @@ -108,4 +113,4 @@ export function activate(context: vscode.ExtensionContext) { ); } -export function deactivate() {} +export function deactivate() { } diff --git a/src/extract/evaluate.test.ts b/src/extract/evaluate.test.ts index 87d4a90..c981756 100644 --- a/src/extract/evaluate.test.ts +++ b/src/extract/evaluate.test.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/extract/evaluate.ts b/src/extract/evaluate.ts index 1de359c..13bdf39 100644 --- a/src/extract/evaluate.ts +++ b/src/extract/evaluate.ts @@ -1,3 +1,8 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + import * as errorParser from 'error-stack-parser'; import * as vm from 'vm'; import { IParsedNode, ITestSymbols, NodeKind } from '.'; diff --git a/src/extract/index.ts b/src/extract/index.ts index 8625279..78d3c7c 100644 --- a/src/extract/index.ts +++ b/src/extract/index.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/extract/syntax.test.ts b/src/extract/syntax.test.ts index 5ea1812..eebb244 100644 --- a/src/extract/syntax.test.ts +++ b/src/extract/syntax.test.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/extract/syntax.ts b/src/extract/syntax.ts index 230a86b..74c0ab8 100644 --- a/src/extract/syntax.ts +++ b/src/extract/syntax.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ @@ -63,8 +64,8 @@ export const extractWithAst = (text: string, symbols: ITestSymbols) => { symbols.suite.includes(name) ? NodeKind.Suite : symbols.test.includes(name) - ? NodeKind.Test - : undefined; + ? NodeKind.Test + : undefined; const stack: { node: Node; r: IParsedNode }[] = []; stack.push({ node: undefined, r: { children: [] } } as any); diff --git a/src/iterable.ts b/src/iterable.ts index 64454b8..fcedb8e 100644 --- a/src/iterable.ts +++ b/src/iterable.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/metadata.ts b/src/metadata.ts index afed870..bcf151b 100644 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/outputQueue.ts b/src/outputQueue.ts index 98a0fd0..e3efea2 100644 --- a/src/outputQueue.ts +++ b/src/outputQueue.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/runner.ts b/src/runner.ts index 872caa6..7286451 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ @@ -33,7 +34,7 @@ export class TestRunner { constructor( private readonly smStore: SourceMapStore, private readonly launchConfig: ConfigValue>, - ) {} + ) { } public makeHandler( ctrl: vscode.TestController, @@ -103,8 +104,7 @@ export class TestRunner { const { path } = parsed[1]; if (path.length > 0) { enqueueLine( - `${' '.repeat(path.length - 1)}${styles.green.open} ✓ ${styles.green.close}${ - path[path.length - 1] + `${' '.repeat(path.length - 1)}${styles.green.open} ✓ ${styles.green.close}${path[path.length - 1] }`, ); } @@ -114,8 +114,7 @@ export class TestRunner { ranAnyTest = true; const { file, path } = parsed[1]; enqueueLine( - `${' '.repeat(path.length - 1)}${styles.green.open} ✓ ${styles.green.close}${ - path[path.length - 1] + `${' '.repeat(path.length - 1)}${styles.green.open} ✓ ${styles.green.close}${path[path.length - 1] }`, ); const test = compiledFileTests.lookup(file, path); @@ -131,8 +130,7 @@ export class TestRunner { const tcase = compiledFileTests.lookup(file, path); enqueueLine( - `${' '.repeat(path.length - 1)}${styles.red.open} x ${path.join(' ')}${ - styles.red.close + `${' '.repeat(path.length - 1)}${styles.red.open} x ${path.join(' ')}${styles.red.close }`, ); const rawErr = stack || err; diff --git a/src/source-map-store.ts b/src/source-map-store.ts index e67e860..42a1b71 100644 --- a/src/source-map-store.ts +++ b/src/source-map-store.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/source-map.ts b/src/source-map.ts index 729ce5c..ff48387 100644 --- a/src/source-map.ts +++ b/src/source-map.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/test/testCases/simple.ts b/src/test/testCases/simple.ts index 71f5e72..55a8afc 100644 --- a/src/test/testCases/simple.ts +++ b/src/test/testCases/simple.ts @@ -1,3 +1,8 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + import { expect } from 'chai'; import { promises as fs } from 'fs'; import * as path from 'path'; diff --git a/src/test/testCases/sourceMapped.ts b/src/test/testCases/sourceMapped.ts index 7cdd9db..c70bd63 100644 --- a/src/test/testCases/sourceMapped.ts +++ b/src/test/testCases/sourceMapped.ts @@ -1,3 +1,8 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + import { expect } from 'chai'; import { promises as fs } from 'fs'; import * as path from 'path'; diff --git a/src/test/util.ts b/src/test/util.ts index 54b0367..fcbf414 100644 --- a/src/test/util.ts +++ b/src/test/util.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/test/workspace-runner.ts b/src/test/workspace-runner.ts index c3b21b2..f8e32a4 100644 --- a/src/test/workspace-runner.ts +++ b/src/test/workspace-runner.ts @@ -1,3 +1,8 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + import * as inspector from "inspector"; import Mocha from "mocha"; import * as path from "path"; diff --git a/src/typings/acorn-loose.d.ts b/src/typings/acorn-loose.d.ts index 4603965..1b097b2 100644 --- a/src/typings/acorn-loose.d.ts +++ b/src/typings/acorn-loose.d.ts @@ -1,4 +1,5 @@ /*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ From 0733d11dc45c4b63cb5ec75e2598c79e0a7f8c4e Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 24 Mar 2024 11:36:51 +0100 Subject: [PATCH 02/26] feat: Implement standalone mocha runner and loader --- .esbuild.js | 5 +- .vscodeignore | 5 + README.md | 4 +- package-lock.json | 957 +++++++++++------- package.json | 20 +- src/configurationFile.ts | 209 ++-- src/constants.ts | 28 +- src/controller.ts | 61 +- src/coverage.ts | 65 -- src/errors.ts | 17 +- src/extension.ts | 2 +- src/extract/evaluate.ts | 108 +- src/extract/index.ts | 31 +- src/extract/syntax.ts | 19 +- src/extract/types.ts | 27 + src/metadata.ts | 2 - src/reporter/fullJsonStreamReporter.ts | 66 ++ src/reporter/fullJsonStreamReporterTypes.ts | 50 + src/runner.ts | 89 +- src/source-map.ts | 8 +- src/{ => test}/extract/evaluate.test.ts | 6 +- src/{ => test}/extract/syntax.test.ts | 6 +- src/test/testCases/simple.ts | 4 +- src/test/testCases/sourceMapped.ts | 4 +- src/test/util.ts | 7 +- src/test/workspace-runner.ts | 39 - .../.mocharc.js} | 2 +- tsconfig.json | 11 +- 28 files changed, 1111 insertions(+), 741 deletions(-) delete mode 100644 src/coverage.ts create mode 100644 src/extract/types.ts create mode 100644 src/reporter/fullJsonStreamReporter.ts create mode 100644 src/reporter/fullJsonStreamReporterTypes.ts rename src/{ => test}/extract/evaluate.test.ts (96%) rename src/{ => test}/extract/syntax.test.ts (93%) delete mode 100644 src/test/workspace-runner.ts rename testCases/{simple/.vscode-test.js => sourceMapped/.mocharc.js} (60%) 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/.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/README.md b/README.md index a9990ef..274d5ae 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ This is the Mocha extension for VS Code enabling developers to run and debug tes ## Credits -This project started as a fork of the `Extension Test Runner` developed by Microsoft and then was adapted to work with Mocha directly. +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 diff --git a/package-lock.json b/package-lock.json index 98f6bac..1607af7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,16 +10,21 @@ "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,8 +35,8 @@ "@types/picomatch": "^2.3.1", "@types/sinon": "^10.0.19", "@types/split2": "^4.2.1", - "@vscode/test-cli": "^0.0.4", - "@vscode/test-electron": "^2.3.9", + "@types/yargs": "^17.0.32", + "@vscode/test-cli": "^0.0.8", "acorn": "^8.10.0", "chai": "^4.3.10", "esbuild": "^0.19.4", @@ -46,6 +51,11 @@ "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/android-arm": { "version": "0.19.4", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.4.tgz", @@ -415,6 +425,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 +455,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", @@ -491,15 +541,6 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/@types/chai": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.7.tgz", @@ -578,148 +619,99 @@ "@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==", - "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" - } - }, - "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/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/@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==", + "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": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" + "@types/yargs-parser": "*" } }, - "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==", + "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/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/@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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "dev": true, + "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": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "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==", - "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": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@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": ">=10" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "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==", - "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": { - "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" + "@typescript-eslint/types": "7.3.1", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": ">=12" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=12" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@vscode/test-electron": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.9.tgz", - "integrity": "sha512-z3eiChaCQXMqBnk2aHHSEkobmC2VRalFQN0ApOAtydL172zXGxTwGrRtviT5HnUB+Q+G3vtEYFtuQkYqBzYgMA==", + "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, "dependencies": { - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "jszip": "^3.10.1", - "semver": "^7.5.2" + "@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" }, - "engines": { - "node": ">=16" + "bin": { + "vscode-test": "out/bin.mjs" } }, "node_modules/acorn": { @@ -744,18 +736,6 @@ "node": ">=0.4.0" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -780,7 +760,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 +789,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 +832,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 +845,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 +975,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 +998,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 +1017,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 +1028,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 +1044,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,26 +1054,22 @@ "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/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "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/cross-spawn": { "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 +1079,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 +1110,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 +1155,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", @@ -1200,7 +1239,6 @@ "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 +1266,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 +1304,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 +1328,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 +1342,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 +1362,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.*" } @@ -1345,7 +1401,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 +1408,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 +1436,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,44 +1449,23 @@ "he": "bin/he" } }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, + "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/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "engines": { - "node": ">= 6" + "node": ">= 4" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dev": true - }, "node_modules/inflight": { "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 +1474,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 +1492,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 +1500,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 +1508,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 +1519,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" } @@ -1497,24 +1544,56 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "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": { - "@types/istanbul-lib-coverage": "^2.0.6" + "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": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/jackspeak": { @@ -1547,18 +1626,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "dev": true, - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, "node_modules/just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", @@ -1574,20 +1641,10 @@ "node": ">=6" } }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/locate-path": { "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 +1695,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 +1810,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 +1896,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 +2024,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 +2032,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 +2046,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" }, @@ -1867,17 +2056,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, "node_modules/path-exists": { "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 +2068,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 +2076,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 +2111,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 +2132,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" }, @@ -1967,12 +2154,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -1986,6 +2167,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", @@ -1995,21 +2195,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2026,11 +2211,19 @@ "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/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2088,6 +2281,28 @@ "node": "*" } }, + "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": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2098,7 +2313,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 +2327,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" }, @@ -2130,17 +2343,10 @@ "randombytes": "^2.1.0" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true - }, "node_modules/shebang-command": { "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 +2358,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 +2366,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 +2418,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", @@ -2238,15 +2450,6 @@ "node": ">=6" } }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -2356,15 +2559,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 +2577,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 +2640,17 @@ "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/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -2411,7 +2672,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" @@ -2426,11 +2686,18 @@ "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", "dev": true }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "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": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } }, "node_modules/vscode-dts": { "version": "0.3.3", @@ -2448,18 +2715,17 @@ } }, "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 +2825,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 +2838,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 +2885,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 +2892,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 +2911,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 +2918,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 6842b31..de0df81 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,6 @@ "jasmine", "karma" ], - "enabledApiProposals": [ - "testCoverage" - ], "categories": [ "Testing" ], @@ -108,7 +105,7 @@ "vscode:prepublish": "npm run compile", "postinstall": "cd src/typings && vscode-dts main && 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", "watch:esbuild": "npm run clean && node .esbuild.js --watch", "watch": "npm run clean && tsc --watch", "test": "tsc && vscode-test", @@ -123,8 +120,8 @@ "@types/picomatch": "^2.3.1", "@types/sinon": "^10.0.19", "@types/split2": "^4.2.1", - "@vscode/test-cli": "^0.0.4", - "@vscode/test-electron": "^2.3.9", + "@types/yargs": "^17.0.32", + "@vscode/test-cli": "^0.0.8", "acorn": "^8.10.0", "chai": "^4.3.10", "esbuild": "^0.19.4", @@ -141,15 +138,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" } -} \ No newline at end of file +} diff --git a/src/configurationFile.ts b/src/configurationFile.ts index 8c1c706..4589f31 100644 --- a/src/configurationFile.ts +++ b/src/configurationFile.ts @@ -3,35 +3,32 @@ * 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'], -}); +export type IResolvedConfiguration = Mocha.MochaOptions & { "_": string[] | undefined, "node-option": 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 _pathToNode?: string; + private _pathToMocha?: string; + /** Cached read promise, invalided on file change. */ private readPromise?: Promise; @@ -83,14 +80,27 @@ 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(); + 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; + } + + async getMochaSpawnArgs(customArgs: readonly string[]): Promise { + // TODO: resolve from package.json? + this._pathToMocha ??= await this._resolveLocalMochaPath('/bin/mocha.js'); + + return [await this.getPathToNode(), this._pathToMocha, '--config', this.uri.fsPath, ...customArgs]; + } + + public async spawnMocha(args: readonly string[]) { + + const spawnArgs = await this.getMochaSpawnArgs(args); + return await new Promise((resolve, reject) => { - const p = spawn(process.execPath, [cliPath, '--config', this.uri.fsPath, ...args], { + const p = spawn(spawnArgs[0], spawnArgs.slice(1), { cwd: path.dirname(this.uri.fsPath), env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' }, }); @@ -99,59 +109,54 @@ export class ConfigurationFile implements vscode.Disposable { }); } - /** - * 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)); - } - - try { - resolve(JSON.parse(joined)); - } catch { - reject(new ConfigProcessReadError(`invalid JSON: ${joined}`)); - } - }); + private async _resolveLocalMochaPath(suffix?: string): Promise { + this._resolver ??= resolveModule.ResolverFactory.createResolver({ + fileSystem: new resolveModule.CachedInputFileSystem(fs, 4000), + conditionNames: ['node', 'require', 'module'], }); - } - - private async _read() { - const configs = await this.captureCliJson(['--list-configuration']); - return new ConfigurationList(this.uri, configs, this.wf); - } - /** - * 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; + 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)); + 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(); @@ -164,29 +169,31 @@ 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), + ), + }; + } }); } @@ -197,17 +204,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); } } @@ -217,28 +222,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 315d189..f60d224 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,12 +3,11 @@ * 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'], @@ -16,5 +15,22 @@ export const defaultTestSymbols: ITestSymbols = { extractWith: 'evaluation', }; -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 "); +} \ No newline at end of file diff --git a/src/controller.ts b/src/controller.ts index b614859..cc048eb 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -110,7 +110,7 @@ export class Controller { let tree: IParsedNode[]; try { - tree = extract(contents, this.extractMode.value); + tree = await extract(uri.fsPath, contents, this.configFile, this.extractMode.value); } catch (e) { this.deleteFileTests(uri.toString()); return; @@ -123,7 +123,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, @@ -133,7 +132,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, }); @@ -145,10 +143,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); @@ -172,12 +170,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 }))! + const file = last(this.getContainingItemsForFile(start.uri, { compiledFile: uri }))! .item!; diagnosticCollection.delete(start.uri); newTestsInFile.set(node.name, add(file, node, start, end)); @@ -269,37 +267,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) { @@ -318,7 +309,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 9e30615..0000000 --- a/src/coverage.ts +++ /dev/null @@ -1,65 +0,0 @@ -/*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org - * 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/errors.ts b/src/errors.ts index fbdcb43..7655e17 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -6,18 +6,9 @@ /** 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}`); } } @@ -26,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 3555f30..da92c78 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -45,7 +45,7 @@ export function activate(context: vscode.ExtensionContext) { await Promise.all( folders.map(async (folder) => { const files = await vscode.workspace.findFiles( - new vscode.RelativePattern(folder, configFilePattern), + new vscode.RelativePattern(folder, configFilePattern), '**/node_modules/**' ); for (const file of files) { const rel = path.relative(folder.uri.fsPath, path.dirname(file.fsPath)); diff --git a/src/extract/evaluate.ts b/src/extract/evaluate.ts index 13bdf39..a489ef9 100644 --- a/src/extract/evaluate.ts +++ b/src/extract/evaluate.ts @@ -3,22 +3,15 @@ * 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 { ConfigurationFile } from '../configurationFile'; +import { isEsm, isTypeScript } from '../constants'; /** * Honestly kind of amazed this works. We can use a Proxy as our globalThis @@ -32,7 +25,21 @@ 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(filePath: string, code: string, config: ConfigurationFile, 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. @@ -48,7 +55,7 @@ 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(); @@ -59,17 +66,41 @@ export const extractWithEvaluation = (code: string, symbols: ITestSymbols) => { return placeholder(); } - const startLine = frame.lineNumber; - const startColumn = frame.columnNumber || 1; + let startLine = (frame.lineNumber || 1) - 1; + let startColumn = (frame.columnNumber || 1) - 1; // approximate the length of the test case: - const functionLines = String(callback).split('\n'); - const endLine = frame.lineNumber + functionLines.length - 1; + let functionLines = String(callback).split('\n'); + let endLine = startLine + functionLines.length; let endColumn = functionLines[functionLines.length - 1].length; if (endLine === startLine) { endColumn = Number.MAX_SAFE_INTEGER; // assume it takes the entire line of a single-line test case } + if (sourceMap) { + try { + const startMapped = originalPositionFor(sourceMap, { + line: startLine, + column: startColumn + }); + if (startMapped.line !== null) { + startLine = startMapped.line + 1; + startColumn = startMapped.column; + } + const endMapped = originalPositionFor(sourceMap, { + line: endLine, + column: endColumn + }) + if (endMapped.line !== null) { + endLine = endMapped.line + 1; + endColumn = endMapped.column; + } + } + catch (e) { + console.error('error mapping source', e); + } + } + const node: IParsedNode = { name, kind, @@ -93,18 +124,45 @@ export const extractWithEvaluation = (code: string, symbols: ITestSymbols) => { stack.pop(); } } + + return placeholder(); }; 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) { + // TODO[log output]: invalid source map? -> ignore for now + } + } + // 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) { @@ -122,9 +180,11 @@ export const extractWithEvaluation = (code: string, symbols: ITestSymbols) => { }, }); + vm.runInNewContext(code, contextObj, { - timeout: 1000, + timeout: 10000 // TODO: setting? }); return stack[0].children; }; + diff --git a/src/extract/index.ts b/src/extract/index.ts index 78d3c7c..817a7a7 100644 --- a/src/extract/index.ts +++ b/src/extract/index.ts @@ -3,41 +3,22 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ +import { ConfigurationFile } from '../configurationFile'; import { extractWithEvaluation } from './evaluate'; import { extractWithAst } from './syntax'; +import { ITestSymbols } from './types'; -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 (filePath: string, code: string, config: ConfigurationFile, symbols: ITestSymbols) => { if (symbols.extractWith === 'evaluation') { try { - return extractWithEvaluation(code, symbols); + return await extractWithEvaluation(filePath, code, config, symbols); } catch (e) { console.warn('error evaluating, will fallback', e); // fall through } } - return extractWithAst(code, symbols); + return extractWithAst(filePath, code, config, symbols); }; diff --git a/src/extract/syntax.ts b/src/extract/syntax.ts index 74c0ab8..36ea66b 100644 --- a/src/extract/syntax.ts +++ b/src/extract/syntax.ts @@ -3,11 +3,14 @@ * 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 { ConfigurationFile } from '../configurationFile'; +import { isTypeScript } from '../constants'; const enum C { MemberExpression = 'MemberExpression', @@ -18,12 +21,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; @@ -57,8 +64,10 @@ const traverse = ( visitor.leave(node); }; -export const extractWithAst = (text: string, symbols: ITestSymbols) => { - const ast = parse(text, acornOptions); + +export const extractWithAst = (filePath: string, text: string, _config: ConfigurationFile, 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) diff --git a/src/extract/types.ts b/src/extract/types.ts new file mode 100644 index 0000000..870dd8d --- /dev/null +++ b/src/extract/types.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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'; +} + +export const enum NodeKind { + Suite, + Test, +} \ No newline at end of file diff --git a/src/metadata.ts b/src/metadata.ts index bcf151b..6108c2f 100644 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -20,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. */ @@ -48,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/reporter/fullJsonStreamReporter.ts b/src/reporter/fullJsonStreamReporter.ts new file mode 100644 index 0000000..594bbb8 --- /dev/null +++ b/src/reporter/fullJsonStreamReporter.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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), + }; +}; \ No newline at end of file diff --git a/src/reporter/fullJsonStreamReporterTypes.ts b/src/reporter/fullJsonStreamReporterTypes.ts new file mode 100644 index 0000000..5b8f372 --- /dev/null +++ b/src/reporter/fullJsonStreamReporterTypes.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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]; \ No newline at end of file diff --git a/src/runner.ts b/src/runner.ts index 7286451..fa48938 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -3,22 +3,21 @@ * 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'; interface ISpawnOptions { - configIndex: number; config: ConfigurationFile; args: string[]; onLine: (line: string) => void; @@ -39,23 +38,13 @@ export class TestRunner { public makeHandler( ctrl: vscode.TestController, config: ConfigurationFile, - configIndex: number, - debug: boolean, - recordCoverage = false, + debug: boolean ): 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); - } - const { args, compiledFileTests, leafTests } = await this.prepareArguments( ctrl, - config, - baseArgs, + [], request, run, ); @@ -78,7 +67,6 @@ export class TestRunner { run.token.onCancellationRequested(() => spawnCts.cancel()); const spawnOpts: ISpawnOptions = { - configIndex, args, config, onLine: (line) => { @@ -189,9 +177,7 @@ 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 { @@ -224,8 +210,6 @@ export class TestRunner { if (!ranAnyTest) { await vscode.commands.executeCommand('testing.showMostRecentOutput'); - } else { - await coverage?.finalize(run); } await outputQueue.drain(); @@ -233,33 +217,30 @@ 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); + return new Promise((resolve, reject) => { const sessionKey = randomUUID(); const includedSessions = new Set(); const launchConfig = this.launchConfig.value || {}; + Promise.resolve( vscode.debug.startDebugging(config.wf, { ...launchConfig, - type: 'extensionHost', + type: 'node', request: 'launch', - name: thisConfig.config.label || `Extension Test Config #${configIndex + 1}`, + name: `Mocha Test (${config.uri.fsPath})`, + program: spawnArgs[1], args: [ - `--extensionTestsPath=${thisConfig.extensionTestsPath}`, - `--extensionDevelopmentPath=${thisConfig.extensionDevelopmentPath}`, - ...(launchConfig.args || []), - ...((thisConfig.config as IDesktopTestConfiguration).launchArgs || []), + ...spawnArgs.slice(2), + ...(launchConfig.args || []) ], - env: { ...thisConfig.env, ...launchConfig.env }, + env: { ...launchConfig.env }, + __extensionSessionKey: sessionKey, }), ).catch(reject); @@ -322,11 +303,13 @@ export class TestRunner { }, }), ); - }).finally(() => ds.dispose()); + }).finally(() => { + ds.dispose() + }); } private async runWithoutDebug({ args, config, onLine, token }: ISpawnOptions) { - const cli = await config.spawnCli(args); + const cli = await config.spawnMocha(args); if (token.isCancellationRequested) { return cli.kill(); } @@ -353,12 +336,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(); @@ -399,6 +381,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); @@ -425,6 +408,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); @@ -454,14 +439,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.ts b/src/source-map.ts index ff48387..372e0ec 100644 --- a/src/source-map.ts +++ b/src/source-map.ts @@ -22,7 +22,7 @@ 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)); }, }); @@ -31,11 +31,11 @@ const smMappingAccessor = (file: vscode.Uri, sm: TraceMap): IMappingAccessor => const { source, line: smLine, column: smCol } = originalPositionFor(sm, { line, 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)); - }, + return new vscode.Location(vscode.Uri.parse(source), new vscode.Position(smLine, smCol)); + } }); export const parseSourceMap = ( diff --git a/src/extract/evaluate.test.ts b/src/test/extract/evaluate.test.ts similarity index 96% rename from src/extract/evaluate.test.ts rename to src/test/extract/evaluate.test.ts index c981756..2505b41 100644 --- a/src/extract/evaluate.test.ts +++ b/src/test/extract/evaluate.test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------*/ 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'; describe('evaluate', () => { it('extracts basic suite', () => { diff --git a/src/extract/syntax.test.ts b/src/test/extract/syntax.test.ts similarity index 93% rename from src/extract/syntax.test.ts rename to src/test/extract/syntax.test.ts index eebb244..36a0a89 100644 --- a/src/extract/syntax.test.ts +++ b/src/test/extract/syntax.test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------*/ import { expect } from 'chai'; -import { NodeKind } from '.'; -import { defaultTestSymbols } from '../constants'; -import { extractWithAst } from './syntax'; +import { defaultTestSymbols } from '../../constants'; +import { NodeKind } from '../../extract'; +import { extractWithAst } from '../../extract/syntax'; describe('syntax', () => { it('extracts basic suite', () => { diff --git a/src/test/testCases/simple.ts b/src/test/testCases/simple.ts index 55a8afc..0b24888 100644 --- a/src/test/testCases/simple.ts +++ b/src/test/testCases/simple.ts @@ -182,12 +182,12 @@ it('handles file and directory excludes', async () => { }); }); -it('handles changes to .vscode-test.js', () => +it('handles changes to .mocharc.js', () => saveAndRestoreWorkspace(folder, async () => { const c = await getController(); const onChange = onceChanged(c); - const configPath = path.join(folder, '.vscode-test.js'); + const configPath = path.join(folder, '.mocharc.js'); const original = await fs.readFile(configPath, 'utf-8'); let updated = original.replace('**/*.test.js', '*.test.js'); diff --git a/src/test/testCases/sourceMapped.ts b/src/test/testCases/sourceMapped.ts index c70bd63..b20be5b 100644 --- a/src/test/testCases/sourceMapped.ts +++ b/src/test/testCases/sourceMapped.ts @@ -143,12 +143,12 @@ it('handles file and directory excludes', async () => { }); }); -it('handles changes to .vscode-test.js', () => +it('handles changes to .mocharc.js', () => saveAndRestoreWorkspace(folder, async () => { const c = await getController(); const onChange = onceChanged(c); - const configPath = path.join(folder, '.vscode-test.js'); + const configPath = path.join(folder, '.mocharc.js'); const original = await fs.readFile(configPath, 'utf-8'); let updated = original.replace('**/*.test.js', '*.test.js'); diff --git a/src/test/util.ts b/src/test/util.ts index fcbf414..3922496 100644 --- a/src/test/util.ts +++ b/src/test/util.ts @@ -72,7 +72,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 rmrf(path.join(original, '.mocha-test')); await fs.cp(original, backup, { recursive: true }); try { @@ -97,6 +97,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 = []; @@ -173,6 +174,10 @@ 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 } diff --git a/src/test/workspace-runner.ts b/src/test/workspace-runner.ts deleted file mode 100644 index f8e32a4..0000000 --- a/src/test/workspace-runner.ts +++ /dev/null @@ -1,39 +0,0 @@ -/*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -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/testCases/simple/.vscode-test.js b/testCases/sourceMapped/.mocharc.js similarity index 60% rename from testCases/simple/.vscode-test.js rename to testCases/sourceMapped/.mocharc.js index 7c50074..a026c28 100644 --- a/testCases/simple/.vscode-test.js +++ b/testCases/sourceMapped/.mocharc.js @@ -1,6 +1,6 @@ module.exports = { files: '**/*.test.js', - cachePath: `${__dirname}/../../.vscode-test`, + cachePath: `${__dirname}/../../.mocha-test`, mocha: { ui: 'bdd' }, }; 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 From 93fd534aa67807cfef2c24edcdea425e47921984 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 24 Mar 2024 23:13:32 +0100 Subject: [PATCH 03/26] chore: Extend tests --- .editorconfig | 6 + .gitignore | 1 + .vscode-test.mjs | 25 +- .vscode/settings.json | 12 +- README.md | 2 +- SECURITY.md | 41 -- package-lock.json | 391 ++++++++++++++---- package.json | 2 + src/configurationFile.ts | 18 + src/controller.ts | 2 +- src/extract/evaluate.ts | 11 +- src/extract/index.ts | 7 +- src/extract/syntax.ts | 11 +- src/test/integration/simple.test.ts | 206 +++++++++ src/test/integration/typescript.test.ts | 65 +++ src/test/testCases/simple.ts | 208 ---------- src/test/testCases/sourceMapped.ts | 169 -------- src/test/{ => unit}/extract/evaluate.test.ts | 150 ++++--- src/test/{ => unit}/extract/syntax.test.ts | 92 +++-- src/test/util.ts | 85 ++-- test-workspaces/simple/.mocharc.js | 3 + .../simple/folder/nested.test.js | 0 .../simple/goodbye.test.js | 0 .../simple/hello.test.js | 0 test-workspaces/typescript/.mocharc.js | 11 + test-workspaces/typescript/hello.test.ts | 21 + testCases/sourceMapped/.mocharc.js | 6 - testCases/sourceMapped/.vscode-test.js | 6 - testCases/sourceMapped/folder/nested.test.js | 3 - .../sourceMapped/folder/nested.test.js.map | 1 - testCases/sourceMapped/folder/nested.test.ts | 1 - testCases/sourceMapped/goodbye.test.js | 9 - testCases/sourceMapped/goodbye.test.js.map | 1 - testCases/sourceMapped/goodbye.test.ts | 8 - testCases/sourceMapped/hello.test.js | 12 - testCases/sourceMapped/hello.test.js.map | 1 - testCases/sourceMapped/hello.test.ts | 11 - testCases/sourceMapped/package.json | 5 - testCases/sourceMapped/tsconfig.json | 17 - 39 files changed, 872 insertions(+), 748 deletions(-) create mode 100644 .editorconfig delete mode 100644 SECURITY.md create mode 100644 src/test/integration/simple.test.ts create mode 100644 src/test/integration/typescript.test.ts delete mode 100644 src/test/testCases/simple.ts delete mode 100644 src/test/testCases/sourceMapped.ts rename src/test/{ => unit}/extract/evaluate.test.ts (54%) rename src/test/{ => unit}/extract/syntax.test.ts (51%) create mode 100644 test-workspaces/simple/.mocharc.js rename {testCases => test-workspaces}/simple/folder/nested.test.js (100%) rename {testCases => test-workspaces}/simple/goodbye.test.js (100%) rename {testCases => test-workspaces}/simple/hello.test.js (100%) create mode 100644 test-workspaces/typescript/.mocharc.js create mode 100644 test-workspaces/typescript/hello.test.ts delete mode 100644 testCases/sourceMapped/.mocharc.js delete mode 100644 testCases/sourceMapped/.vscode-test.js delete mode 100644 testCases/sourceMapped/folder/nested.test.js delete mode 100644 testCases/sourceMapped/folder/nested.test.js.map delete mode 100644 testCases/sourceMapped/folder/nested.test.ts delete mode 100644 testCases/sourceMapped/goodbye.test.js delete mode 100644 testCases/sourceMapped/goodbye.test.js.map delete mode 100644 testCases/sourceMapped/goodbye.test.ts delete mode 100644 testCases/sourceMapped/hello.test.js delete mode 100644 testCases/sourceMapped/hello.test.js.map delete mode 100644 testCases/sourceMapped/hello.test.ts delete mode 100644 testCases/sourceMapped/package.json delete mode 100644 testCases/sourceMapped/tsconfig.json 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/.gitignore b/.gitignore index d9af962..2e02430 100644 --- a/.gitignore +++ b/.gitignore @@ -121,6 +121,7 @@ dist # Stores VSCode versions used for testing VSCode extensions .vscode-test +.working-copy # yarn v2 .yarn/cache diff --git a/.vscode-test.mjs b/.vscode-test.mjs index be4f0fb..ec7b9c4 100644 --- a/.vscode-test.mjs +++ b/.vscode-test.mjs @@ -4,26 +4,33 @@ 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([ { - label: 'core', - files: 'out/**/*.test.js', + platform: vsCodePlatform, + version: vsCodeVersion, + label: 'unit', + files: 'out/test/unit/**/*.test.js', mocha: { ui: 'bdd' }, }, ...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 { + platform: vsCodePlatform, + version: vsCodeVersion, label, - files: path.join(testCaseRunnerDir, file), - workspaceFolder: path.join(dirname, `testCases/${label}`), + files: path.join(integrationTestDir, file), mocha: { ui: 'bdd', timeout: 60_000 }, + workspaceFolder: path.join(workspaceBaseDir, label), }; }), ]); 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/README.md b/README.md index 274d5ae..83d7466 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Mocha VSCode Extension +# Mocha VS Code 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. 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/package-lock.json b/package-lock.json index 1607af7..63ebcfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "@types/split2": "^4.2.1", "@types/yargs": "^17.0.32", "@vscode/test-cli": "^0.0.8", + "@vscode/test-electron": "^2.3.9", "acorn": "^8.10.0", "chai": "^4.3.10", "esbuild": "^0.19.4", @@ -44,6 +45,7 @@ "mocha": "^10.2.0", "prettier": "^3.0.3", "sinon": "^16.1.0", + "tsx": "^4.7.1", "typescript": "^5.2.2", "vscode-dts": "^0.3.3" }, @@ -56,10 +58,26 @@ "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" ], @@ -73,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" ], @@ -89,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" ], @@ -105,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" ], @@ -121,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" ], @@ -137,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" ], @@ -153,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" ], @@ -169,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" ], @@ -185,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" ], @@ -201,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" ], @@ -217,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" ], @@ -233,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" ], @@ -249,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" ], @@ -265,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" ], @@ -281,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" ], @@ -297,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" ], @@ -313,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" ], @@ -329,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" ], @@ -345,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" ], @@ -361,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" ], @@ -377,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" ], @@ -393,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" ], @@ -541,6 +559,15 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/@types/chai": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.7.tgz", @@ -714,6 +741,21 @@ "vscode-test": "out/bin.mjs" } }, + "node_modules/@vscode/test-electron": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.9.tgz", + "integrity": "sha512-z3eiChaCQXMqBnk2aHHSEkobmC2VRalFQN0ApOAtydL172zXGxTwGrRtviT5HnUB+Q+G3vtEYFtuQkYqBzYgMA==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "jszip": "^3.10.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -736,6 +778,18 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -1066,6 +1120,12 @@ "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", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1199,9 +1259,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": { @@ -1211,28 +1271,29 @@ "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": { @@ -1375,6 +1436,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", @@ -1454,6 +1527,33 @@ "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", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -1462,6 +1562,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1544,6 +1650,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, "node_modules/isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", @@ -1626,6 +1738,18 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", @@ -1641,6 +1765,15 @@ "node": ">=6" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2056,6 +2189,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2154,6 +2293,12 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -2195,6 +2340,21 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2215,6 +2375,15 @@ "node": ">=0.10.0" } }, + "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, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -2343,6 +2512,12 @@ "randombytes": "^2.1.0" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2450,6 +2625,15 @@ "node": ">=6" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -2651,6 +2835,25 @@ "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", @@ -2686,6 +2889,12 @@ "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", "dev": true }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "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", diff --git a/package.json b/package.json index de0df81..44f36ef 100644 --- a/package.json +++ b/package.json @@ -122,6 +122,7 @@ "@types/split2": "^4.2.1", "@types/yargs": "^17.0.32", "@vscode/test-cli": "^0.0.8", + "@vscode/test-electron": "^2.3.9", "acorn": "^8.10.0", "chai": "^4.3.10", "esbuild": "^0.19.4", @@ -129,6 +130,7 @@ "mocha": "^10.2.0", "prettier": "^3.0.3", "sinon": "^16.1.0", + "tsx": "^4.7.1", "typescript": "^5.2.2", "vscode-dts": "^0.3.3" }, diff --git a/src/configurationFile.ts b/src/configurationFile.ts index 4589f31..decd296 100644 --- a/src/configurationFile.ts +++ b/src/configurationFile.ts @@ -17,6 +17,10 @@ type OptionsModule = { loadOptions(): IResolvedConfiguration }; +type ConfigModule = { + findConfig(): string +}; + export type IResolvedConfiguration = Mocha.MochaOptions & { "_": string[] | undefined, "node-option": string[] | undefined } export class ConfigurationFile implements vscode.Disposable { @@ -26,6 +30,7 @@ export class ConfigurationFile implements vscode.Disposable { private _resolver?: resolveModule.Resolver; private _optionsModule?: OptionsModule; + private _configModule?: ConfigModule; private _pathToNode?: string; private _pathToMocha?: string; @@ -134,6 +139,7 @@ export class ConfigurationFile implements vscode.Disposable { 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, @@ -141,6 +147,18 @@ export class ConfigurationFile implements vscode.Disposable { 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 { diff --git a/src/controller.ts b/src/controller.ts index cc048eb..bc09e5d 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -110,7 +110,7 @@ export class Controller { let tree: IParsedNode[]; try { - tree = await extract(uri.fsPath, contents, this.configFile, this.extractMode.value); + tree = await extract(uri.fsPath, contents, this.extractMode.value); } catch (e) { this.deleteFileTests(uri.toString()); return; diff --git a/src/extract/evaluate.ts b/src/extract/evaluate.ts index a489ef9..3565ccc 100644 --- a/src/extract/evaluate.ts +++ b/src/extract/evaluate.ts @@ -10,7 +10,6 @@ import { } from 'esbuild'; import * as vm from 'vm'; import { IParsedNode, ITestSymbols, NodeKind } from '.'; -import { ConfigurationFile } from '../configurationFile'; import { isEsm, isTypeScript } from '../constants'; /** @@ -26,7 +25,7 @@ import { isEsm, isTypeScript } from '../constants'; * is also effective in stubbing require() so we know code is nicely isolated. */ -export async function extractWithEvaluation(filePath: string, code: string, config: ConfigurationFile, symbols: ITestSymbols) { +export async function extractWithEvaluation(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 @@ -71,7 +70,7 @@ export async function extractWithEvaluation(filePath: string, code: string, conf // approximate the length of the test case: let functionLines = String(callback).split('\n'); - let endLine = startLine + functionLines.length; + let endLine = startLine + functionLines.length - 1; let endColumn = functionLines[functionLines.length - 1].length; if (endLine === startLine) { endColumn = Number.MAX_SAFE_INTEGER; // assume it takes the entire line of a single-line test case @@ -108,7 +107,7 @@ export async function extractWithEvaluation(filePath: string, code: string, conf startColumn, endLine, endColumn, - children: [], + children: [] }; if (directive) { node.directive = directive; @@ -117,15 +116,13 @@ export async function extractWithEvaluation(filePath: string, code: string, conf 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 { stack.pop(); } } - - return placeholder(); }; if (!directive) { fn.skip = makeTesterFunction(kind, sourceMap, 'skip'); diff --git a/src/extract/index.ts b/src/extract/index.ts index 817a7a7..9e76263 100644 --- a/src/extract/index.ts +++ b/src/extract/index.ts @@ -3,22 +3,21 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ -import { ConfigurationFile } from '../configurationFile'; import { extractWithEvaluation } from './evaluate'; import { extractWithAst } from './syntax'; import { ITestSymbols } from './types'; export * from './types'; -export const extract = async (filePath: string, code: string, config: ConfigurationFile, symbols: ITestSymbols) => { +export const extract = async (filePath: string, code: string, symbols: ITestSymbols) => { if (symbols.extractWith === 'evaluation') { try { - return await extractWithEvaluation(filePath, code, config, symbols); + return await extractWithEvaluation(filePath, code, symbols); } catch (e) { console.warn('error evaluating, will fallback', e); // fall through } } - return extractWithAst(filePath, code, config, symbols); + return extractWithAst(filePath, code, symbols); }; diff --git a/src/extract/syntax.ts b/src/extract/syntax.ts index 36ea66b..dcfaf54 100644 --- a/src/extract/syntax.ts +++ b/src/extract/syntax.ts @@ -9,7 +9,6 @@ import { parse as acornParse } from 'acorn-loose'; import * as evk from 'eslint-visitor-keys'; import { Node } from 'estree'; import { IParsedNode, ITestSymbols, NodeKind } from '.'; -import { ConfigurationFile } from '../configurationFile'; import { isTypeScript } from '../constants'; const enum C { @@ -65,7 +64,7 @@ const traverse = ( }; -export const extractWithAst = (filePath: string, text: string, _config: ConfigurationFile, symbols: ITestSymbols) => { +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; @@ -109,10 +108,10 @@ export const extractWithAst = (filePath: string, text: string, _config: Configur 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/test/integration/simple.test.ts b/src/test/integration/simple.test.ts new file mode 100644 index 0000000..bd25903 --- /dev/null +++ b/src/test/integration/simple.test.ts @@ -0,0 +1,206 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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']]]]], + ]); + }); +}); \ No newline at end of file diff --git a/src/test/integration/typescript.test.ts b/src/test/integration/typescript.test.ts new file mode 100644 index 0000000..91b09f0 --- /dev/null +++ b/src/test/integration/typescript.test.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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', () => { + const workspaceFolder = integrationTestPrepare('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'], + }); + }); +}); \ No newline at end of file diff --git a/src/test/testCases/simple.ts b/src/test/testCases/simple.ts deleted file mode 100644 index 0b24888..0000000 --- a/src/test/testCases/simple.ts +++ /dev/null @@ -1,208 +0,0 @@ -/*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org - * 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, - 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 .mocharc.js', () => - saveAndRestoreWorkspace(folder, async () => { - const c = await getController(); - const onChange = onceChanged(c); - - const configPath = path.join(folder, '.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/testCases/sourceMapped.ts b/src/test/testCases/sourceMapped.ts deleted file mode 100644 index b20be5b..0000000 --- a/src/test/testCases/sourceMapped.ts +++ /dev/null @@ -1,169 +0,0 @@ -/*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org - * 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, - 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 .mocharc.js', () => - saveAndRestoreWorkspace(folder, async () => { - const c = await getController(); - const onChange = onceChanged(c); - - const configPath = path.join(folder, '.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.ts', [['math', [['division']]]]], - ['hello.test.ts', [['math', [['addition'], ['subtraction']]]]], - ]); - })); diff --git a/src/test/extract/evaluate.test.ts b/src/test/unit/extract/evaluate.test.ts similarity index 54% rename from src/test/extract/evaluate.test.ts rename to src/test/unit/extract/evaluate.test.ts index 2505b41..41cd63e 100644 --- a/src/test/extract/evaluate.test.ts +++ b/src/test/unit/extract/evaluate.test.ts @@ -4,34 +4,37 @@ *--------------------------------------------------------*/ import { expect } from 'chai'; -import { defaultTestSymbols } from '../../constants'; -import { NodeKind } from '../../extract'; -import { extractWithEvaluation } from '../../extract/evaluate'; +import { defaultTestSymbols } from '../../../constants'; +import { NodeKind } from '../../../extract'; +import { extractWithEvaluation } from '../../../extract/evaluate'; describe('evaluate', () => { - it('extracts basic suite', () => { - const src = extractWithEvaluation( - `suite('hello', () => { - it('works', () => {}); - })`, + it('extracts basic suite', async () => { + const src = await extractWithEvaluation( + 'test.js', + [ + "suite('hello', () => {", + " it('works', () => {});", + "})" + ].join('\n'), 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: [], }, ], @@ -39,48 +42,51 @@ 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( + 'test.js', + [ + "suite('hello', () => {", + " for (const name of ['foo', 'bar', 'baz']) {", + " it(name, () => {});", + " }", + "})" + ].join('\n'), 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: [], }, @@ -88,49 +94,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( + 'test.js', + [ + "suite('hello', () => {", + " throw new Error('whoops');", + "})" + ].join('\n') + , 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( + 'test.js', + [ + "suite('hello', () => {", + " it.only('a', ()=>{});", + " it.skip('a', ()=>{});", + "})" + ].join('\n') + , 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', @@ -138,9 +152,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', @@ -150,16 +164,18 @@ describe('evaluate', () => { ]); }); - it('stubs out requires and placeholds correctly', () => { - const src = extractWithEvaluation( + it('stubs out requires and placeholds correctly', async () => { + const src = await extractWithEvaluation( + '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( + 'test.js', `var foo = () => suite('hello', () => {}); foo();`, defaultTestSymbols, ); @@ -167,9 +183,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/extract/syntax.test.ts b/src/test/unit/extract/syntax.test.ts similarity index 51% rename from src/test/extract/syntax.test.ts rename to src/test/unit/extract/syntax.test.ts index 36a0a89..ee02d76 100644 --- a/src/test/extract/syntax.test.ts +++ b/src/test/unit/extract/syntax.test.ts @@ -4,34 +4,37 @@ *--------------------------------------------------------*/ import { expect } from 'chai'; -import { defaultTestSymbols } from '../../constants'; -import { NodeKind } from '../../extract'; -import { extractWithAst } from '../../extract/syntax'; +import { defaultTestSymbols } from '../../../constants'; +import { NodeKind } from '../../../extract'; +import { extractWithAst } from '../../../extract/syntax'; describe('syntax', () => { it('extracts basic suite', () => { const src = extractWithAst( - `suite('hello', () => { - it('works', () => {}); - })`, + 'test.js', + [ + "suite('hello', () => {", + " it('works', () => {});", + "})" + ].join('\n'), defaultTestSymbols, ); expect(src).to.deep.equal([ { name: 'hello', - startLine: 1, + startLine: 0, kind: NodeKind.Suite, - startColumn: 1, - endColumn: 7, - endLine: 3, + startColumn: 0, + endColumn: 2, + endLine: 2, children: [ { name: 'works', kind: NodeKind.Test, - startLine: 2, - startColumn: 7, - endColumn: 28, - endLine: 2, + startLine: 1, + startColumn: 2, + endColumn: 23, + endLine: 1, children: [], }, ], @@ -41,38 +44,41 @@ describe('syntax', () => { it('works with skip/only', () => { const src = extractWithAst( - `suite('hello', () => { - it.only('a', ()=>{}); - it.skip('a', ()=>{}); - })`, + 'test.js', + [ + "suite('hello', () => {", + " it.only('a', ()=>{});", + " it.skip('a', ()=>{});", + "})" + ].join('\n'), defaultTestSymbols, ); expect(src).to.deep.equal([ { name: 'hello', kind: NodeKind.Suite, - startLine: 1, - startColumn: 1, - endColumn: 7, - endLine: 4, + startLine: 0, + startColumn: 0, + endColumn: 2, + endLine: 3, children: [ { name: 'a', kind: NodeKind.Test, - startLine: 2, - startColumn: 9, - endColumn: 29, - endLine: 2, + startLine: 1, + startColumn: 2, + endColumn: 22, + endLine: 1, children: [], directive: 'only', }, { name: 'a', kind: NodeKind.Test, - startLine: 3, - startColumn: 9, - endColumn: 29, - endLine: 3, + startLine: 2, + startColumn: 2, + endColumn: 22, + endLine: 2, children: [], directive: 'skip', }, @@ -81,8 +87,34 @@ describe('syntax', () => { ]); }); + it('can detect suite but not dynamic tests', () => { + const src = extractWithAst( + 'test.js', + [ + "suite('hello', () => {", + " for (const name of ['foo', 'bar', 'baz']) {", + " it(name, () => {});", + " }", + "})" + ].join('\n'), + 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, ); diff --git a/src/test/util.ts b/src/test/util.ts index 3922496..9e01850 100644 --- a/src/test/util.ts +++ b/src/test/util.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------*/ 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'; @@ -14,7 +14,49 @@ import * as vscode from 'vscode'; import { getControllersForTestCommand } from '../constants'; import type { Controller } from '../controller'; -export const getController = async () => { +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) { @@ -28,7 +70,7 @@ export const getController = async () => { 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); @@ -42,25 +84,27 @@ 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) { @@ -70,27 +114,6 @@ 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, '.mocha-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'; export class FakeTestRun implements vscode.TestRun { @@ -181,7 +204,7 @@ export class FakeTestRun implements vscode.TestRun { //#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 { 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/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/sourceMapped/.mocharc.js b/testCases/sourceMapped/.mocharc.js deleted file mode 100644 index a026c28..0000000 --- a/testCases/sourceMapped/.mocharc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - files: '**/*.test.js', - cachePath: `${__dirname}/../../.mocha-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/testCases/sourceMapped/hello.test.js b/testCases/sourceMapped/hello.test.js deleted file mode 100644 index d8514e4..0000000 --- a/testCases/sourceMapped/hello.test.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const node_assert_1 = require("node:assert"); -describe('math', () => { - it('addition', async () => { - (0, node_assert_1.strictEqual)(1 + 1, 2); - }); - it(`subtraction`, async () => { - (0, node_assert_1.strictEqual)(1 - 1, 0); - }); -}); -//# sourceMappingURL=hello.test.js.map \ No newline at end of file diff --git a/testCases/sourceMapped/hello.test.js.map b/testCases/sourceMapped/hello.test.js.map deleted file mode 100644 index e980d88..0000000 --- a/testCases/sourceMapped/hello.test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"hello.test.js","sourceRoot":"","sources":["hello.test.ts"],"names":[],"mappings":";;AAAA,6CAA0C;AAE1C,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;IAEH,EAAE,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;QAC3B,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/hello.test.ts b/testCases/sourceMapped/hello.test.ts deleted file mode 100644 index b01f2ba..0000000 --- a/testCases/sourceMapped/hello.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { strictEqual } from 'node:assert'; - -describe('math', () => { - it('addition', async () => { - strictEqual(1 + 1, 2); - }); - - it(`subtraction`, async () => { - strictEqual(1 - 1, 0); - }); -}); diff --git a/testCases/sourceMapped/package.json b/testCases/sourceMapped/package.json deleted file mode 100644 index 2e3ec81..0000000 --- a/testCases/sourceMapped/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "scripts": { - "build": "tsc -p tsconfig.json" - } -} diff --git a/testCases/sourceMapped/tsconfig.json b/testCases/sourceMapped/tsconfig.json deleted file mode 100644 index 68846bc..0000000 --- a/testCases/sourceMapped/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "ES2021", - "lib": ["ES2021"], - "sourceMap": true, - "skipLibCheck": true, - "strict": true /* enable all strict type-checking options */, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true - /* Additional Checks */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - }, - "include": ["."] -} From 619f562f60b167d813d0f4cac5c79aff3880557e Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 16:39:10 +0200 Subject: [PATCH 04/26] build: add test reports --- .github/workflows/build.yml | 7 +++ .gitignore | 1 + .vscode-ci-test-reporter.js | 19 ++++++ .vscode-test.mjs | 40 ++++++++++--- package-lock.json | 115 +++++++++++++----------------------- package.json | 4 +- 6 files changed, 103 insertions(+), 83 deletions(-) create mode 100644 .vscode-ci-test-reporter.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 160fa0f..638143a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,3 +26,10 @@ jobs: if: runner.os == 'Linux' - run: npm test if: runner.os != 'Linux' + - uses: dorny/test-reporter@v1 + if: always() + with: + artifact: test-results + name: VS Code Tests + path: 'test-results/*.json' + reporter: mocha-json diff --git a/.gitignore b/.gitignore index 2e02430..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 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 ec7b9c4..5cdbac6 100644 --- a/.vscode-test.mjs +++ b/.vscode-test.mjs @@ -7,17 +7,45 @@ const dirname = fileURLToPath(new URL('.', import.meta.url)); const integrationTestDir = path.join(dirname, 'out/test/integration'); const workspaceBaseDir = path.join(dirname, 'test-workspaces'); - const vsCodeVersion = process.env.VSCODE_TEST_VERSION ?? 'stable'; const vsCodePlatform = process.env.VSCODE_TEST_PLATFORM ?? 'desktop'; +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`), + }, + }, + }, + }; + } else { + return { + platform: vsCodePlatform, + version: vsCodeVersion, + + mocha: { + ui: 'bdd', + }, + }; + } +}; + export default defineConfig([ { - platform: vsCodePlatform, - version: vsCodeVersion, label: 'unit', files: 'out/test/unit/**/*.test.js', - mocha: { ui: 'bdd' }, + ...createCommonOptions('unit'), }, ...fs .readdirSync(integrationTestDir) @@ -25,12 +53,10 @@ export default defineConfig([ .map((file) => { const label = path.basename(file, '.test.js'); return { - platform: vsCodePlatform, - version: vsCodeVersion, label, files: path.join(integrationTestDir, file), - mocha: { ui: 'bdd', timeout: 60_000 }, workspaceFolder: path.join(workspaceBaseDir, label), + ...createCommonOptions(label), }; }), ]); diff --git a/package-lock.json b/package-lock.json index 63ebcfe..7c80911 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "@types/sinon": "^10.0.19", "@types/split2": "^4.2.1", "@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", @@ -46,8 +47,7 @@ "prettier": "^3.0.3", "sinon": "^16.1.0", "tsx": "^4.7.1", - "typescript": "^5.2.2", - "vscode-dts": "^0.3.3" + "typescript": "^5.2.2" }, "engines": { "vscode": "^1.83.0" @@ -721,6 +721,45 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "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, + "dependencies": { + "https-proxy-agent": "^7.0.0", + "minimist": "^1.2.8", + "prompts": "^2.4.2" + }, + "bin": { + "dts": "index.js" + } + }, + "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": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "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": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/@vscode/test-cli": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.8.tgz", @@ -2393,63 +2432,6 @@ "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==", - "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" - } - }, - "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" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2908,21 +2890,6 @@ "node": ">=10.12.0" } }, - "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, - "dependencies": { - "minimist": "^1.2.0", - "prompts": "^2.1.0", - "rimraf": "^3.0.0" - }, - "bin": { - "vscode-dts": "index.js" - } - }, "node_modules/which": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", diff --git a/package.json b/package.json index 44f36ef..9bd9f82 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "@types/sinon": "^10.0.19", "@types/split2": "^4.2.1", "@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", @@ -131,8 +132,7 @@ "prettier": "^3.0.3", "sinon": "^16.1.0", "tsx": "^4.7.1", - "typescript": "^5.2.2", - "vscode-dts": "^0.3.3" + "typescript": "^5.2.2" }, "prettier": { "printWidth": 100, From 1e2d0b482e23793a8dea7a4e64bb84254825d4f7 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 16:42:50 +0200 Subject: [PATCH 05/26] build: Fix vscode DTS --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9bd9f82..a993493 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "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 && tsc --noEmit && node .esbuild.js --minify", "watch:esbuild": "npm run clean && node .esbuild.js --watch", From 372eb1744a5cf14e26d3a5cc5dd3306fa1672873 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 16:47:56 +0200 Subject: [PATCH 06/26] build: don't fail fast --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 638143a..589f2ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,6 +9,7 @@ jobs: build: name: Build and Test strategy: + fail-fast: false matrix: os: [macos-latest, ubuntu-latest, windows-latest] vscode-version: ['stable', 'insiders'] From 6eeefd12135325103f1311970b4239ecccc557e1 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 16:53:01 +0200 Subject: [PATCH 07/26] build: increase timeout of tests --- .vscode-test.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode-test.mjs b/.vscode-test.mjs index 5cdbac6..f5e6371 100644 --- a/.vscode-test.mjs +++ b/.vscode-test.mjs @@ -27,6 +27,7 @@ let createCommonOptions = (label) => { output: path.join(dirname, 'test-results', `${label}.json`), }, }, + timeout: 60_000, }, }; } else { From 105289cd7b7a0fa592ab207d46752ae2cd70a4d0 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 16:56:02 +0200 Subject: [PATCH 08/26] build: Add test config printing --- .github/workflows/build.yml | 4 ++++ .vscode-test.mjs | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 589f2ee..9f94020 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,10 @@ jobs: with: node-version: lts/* - run: npm install + - 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 diff --git a/.vscode-test.mjs b/.vscode-test.mjs index f5e6371..855072c 100644 --- a/.vscode-test.mjs +++ b/.vscode-test.mjs @@ -42,7 +42,7 @@ let createCommonOptions = (label) => { } }; -export default defineConfig([ +const config = [ { label: 'unit', files: 'out/test/unit/**/*.test.js', @@ -60,4 +60,10 @@ export default defineConfig([ ...createCommonOptions(label), }; }), -]); +]; + +if (process.env.VSCODE_CONFIG_LOG) { + console.log(config); +} + +export default defineConfig(config); From 33f01740af2aa837b6be3fb06bcf20a05e79a47d Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 16:57:14 +0200 Subject: [PATCH 09/26] build: explicit compile call --- .github/workflows/build.yml | 1 + package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9f94020..99874bd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,7 @@ jobs: with: node-version: lts/* - run: npm install + - run: npm run compile:test - name: Print Test Config run: node ./.vscode-test.mjs env: diff --git a/package.json b/package.json index a993493..05362a8 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,7 @@ "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 && 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", From 5ecc515e73e32310833e3d26a5980892840c34d5 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 17:06:19 +0200 Subject: [PATCH 10/26] build: test output to check for files --- .github/workflows/build.yml | 2 ++ .vscode-test.mjs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 99874bd..cbcf453 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,6 +32,8 @@ jobs: if: runner.os == 'Linux' - run: npm test if: runner.os != 'Linux' + - run: tree -L 2 + if: runner.os == 'Linux' - uses: dorny/test-reporter@v1 if: always() with: diff --git a/.vscode-test.mjs b/.vscode-test.mjs index 855072c..50a4667 100644 --- a/.vscode-test.mjs +++ b/.vscode-test.mjs @@ -63,7 +63,8 @@ const config = [ ]; if (process.env.VSCODE_CONFIG_LOG) { - console.log(config); + const util = require('util'); + console.log(util.inspect(config, { showHidden: false, depth: null, colors: true })); } export default defineConfig(config); From c32cb75b7a7dde8c844901cc35e6e6ffb675bcca Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 17:07:23 +0200 Subject: [PATCH 11/26] build: fix improt --- .vscode-test.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode-test.mjs b/.vscode-test.mjs index 50a4667..99a08a4 100644 --- a/.vscode-test.mjs +++ b/.vscode-test.mjs @@ -63,7 +63,7 @@ const config = [ ]; if (process.env.VSCODE_CONFIG_LOG) { - const util = require('util'); + const util = await import('util'); console.log(util.inspect(config, { showHidden: false, depth: null, colors: true })); } From 39d8693c482b781c800363123018818c0b201f77 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 17:10:21 +0200 Subject: [PATCH 12/26] build: Remove artifact name --- .github/workflows/build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cbcf453..68b794c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,12 +32,9 @@ jobs: if: runner.os == 'Linux' - run: npm test if: runner.os != 'Linux' - - run: tree -L 2 - if: runner.os == 'Linux' - uses: dorny/test-reporter@v1 if: always() with: - artifact: test-results name: VS Code Tests path: 'test-results/*.json' reporter: mocha-json From 0d7ac25e83242d122298be89ac4df0e627061b14 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 17:12:56 +0200 Subject: [PATCH 13/26] build: Permissions and matrix handling --- .github/workflows/build.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68b794c..415651d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,6 +5,11 @@ on: pull_request: workflow_dispatch: +permissions: + contents: read + actions: read + checks: write + jobs: build: name: Build and Test @@ -15,6 +20,9 @@ jobs: 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 @@ -35,6 +43,6 @@ jobs: - uses: dorny/test-reporter@v1 if: always() with: - name: VS Code Tests + name: VS Code Tests ${{matrix.os}}-${{matrix.vscode-version}}-${{matrix.vscode-platform}} path: 'test-results/*.json' reporter: mocha-json From a36d8e2233be71c20c295906eeb8e4354dca955e Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 17:15:01 +0200 Subject: [PATCH 14/26] build: Align formatting of test results --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 415651d..f0e7db0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,6 +43,6 @@ jobs: - uses: dorny/test-reporter@v1 if: always() with: - name: VS Code Tests ${{matrix.os}}-${{matrix.vscode-version}}-${{matrix.vscode-platform}} + name: VS Code Test Results (${{matrix.os}}, ${{matrix.vscode-version}}, ${{matrix.vscode-platform}}) path: 'test-results/*.json' reporter: mocha-json From 5390451140da4f9079a076cc4b5e6d62a447a75b Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 17:42:47 +0200 Subject: [PATCH 15/26] chore: Cleanup and todo resolving --- README.md | 12 ++-- package.json | 11 +++- src/configurationFile.ts | 64 +++++++++++---------- src/constants.ts | 14 +++-- src/controller.ts | 10 ++-- src/errors.ts | 2 +- src/extension.ts | 36 +++++------- src/extract/evaluate.ts | 44 +++++++------- src/extract/index.ts | 10 +++- src/extract/syntax.ts | 11 ++-- src/extract/types.ts | 31 +++++----- src/reporter/fullJsonStreamReporter.ts | 6 +- src/reporter/fullJsonStreamReporterTypes.ts | 50 ++++++++-------- src/runner.ts | 48 +++++++++++----- src/source-map.ts | 2 +- src/test/integration/simple.test.ts | 2 +- src/test/integration/typescript.test.ts | 13 +---- src/test/unit/extract/evaluate.test.ts | 35 +++++------ src/test/unit/extract/syntax.test.ts | 21 +++---- src/test/util.ts | 16 +++--- 20 files changed, 226 insertions(+), 212 deletions(-) diff --git a/README.md b/README.md index 83d7466..1e1de4b 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,12 @@ This is the Mocha extension for VS Code enabling developers to run and debug tes ## 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 +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 @@ -26,6 +25,7 @@ This extension automatically discovers and works with the `.mocharc.js/cjs/yaml/ - `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. - `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/package.json b/package.json index 05362a8..ba7d899 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "title": "Extension Test Runner", "properties": { "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 `test` and `suite` identifiers the process extracts.", + "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": { @@ -59,6 +59,9 @@ "evaluation", "syntax" ] + }, + "extractTimeout": { + "type": "number" } }, "default": { @@ -70,12 +73,14 @@ "it", "test" ], - "extractWith": "evaluation" + "extractWith": "evaluation", + "extractTimeout": 10000 }, "required": [ "suite", "test", - "extractWith" + "extractWith", + "extractTimeout" ] }, "mocha-vscode.debugOptions": { diff --git a/src/configurationFile.ts b/src/configurationFile.ts index decd296..fa02dfd 100644 --- a/src/configurationFile.ts +++ b/src/configurationFile.ts @@ -14,14 +14,17 @@ import { DisposableStore } from './disposable'; import { HumanError } from './errors'; type OptionsModule = { - loadOptions(): IResolvedConfiguration + loadOptions(): IResolvedConfiguration; }; type ConfigModule = { - findConfig(): string + findConfig(): string; }; -export type IResolvedConfiguration = Mocha.MochaOptions & { "_": string[] | undefined, "node-option": string[] | undefined } +export type IResolvedConfiguration = Mocha.MochaOptions & { + _: string[] | undefined; + 'node-option': string[] | undefined; +}; export class ConfigurationFile implements vscode.Disposable { private readonly ds = new DisposableStore(); @@ -89,29 +92,20 @@ export class ConfigurationFile implements vscode.Disposable { // 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; + this._pathToNode ??= (await which('node', { nothrow: true })) ?? process.execPath; return this._pathToNode; } async getMochaSpawnArgs(customArgs: readonly string[]): Promise { - // TODO: resolve from package.json? this._pathToMocha ??= await this._resolveLocalMochaPath('/bin/mocha.js'); - return [await this.getPathToNode(), this._pathToMocha, '--config', this.uri.fsPath, ...customArgs]; - } - - public async spawnMocha(args: readonly string[]) { - - const spawnArgs = await this.getMochaSpawnArgs(args); - - return await new Promise((resolve, reject) => { - const p = spawn(spawnArgs[0], spawnArgs.slice(1), { - cwd: path.dirname(this.uri.fsPath), - env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' }, - }); - p.on('spawn', () => resolve(p)); - p.on('error', reject); - }); + return [ + await this.getPathToNode(), + this._pathToMocha, + '--config', + this.uri.fsPath, + ...customArgs, + ]; } private async _resolveLocalMochaPath(suffix?: string): Promise { @@ -124,11 +118,17 @@ export class ConfigurationFile implements vscode.Disposable { this._resolver!.resolve( {}, path.dirname(this.uri.fsPath), - 'mocha' + (suffix ?? ""), + 'mocha' + (suffix ?? ''), {}, (err, res) => { if (err) { - reject(new HumanError(`Could not find mocha in working directory '${path.dirname(this.uri.fsPath)}', please install mocha to run tests.`)); + reject( + new HumanError( + `Could not find mocha in working directory '${path.dirname( + this.uri.fsPath, + )}', please install mocha to run tests.`, + ), + ); } else { resolve(res as string); } @@ -138,13 +138,17 @@ export class ConfigurationFile implements vscode.Disposable { } 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; + 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, + // need to change to the working dir for loading the config, // TODO[mocha]: allow specifying the cwd in loadOptions() - const currentCwd = process.cwd();; + const currentCwd = process.cwd(); try { process.chdir(path.dirname(this.uri.fsPath)); @@ -154,14 +158,12 @@ export class ConfigurationFile implements vscode.Disposable { try { const resolved = require.resolve(configFile); delete require.cache[resolved]; - } - catch (e) { + } catch (e) { // ignore } config = this._optionsModule.loadOptions(); - } - finally { + } finally { process.chdir(currentCwd); } @@ -199,7 +201,7 @@ export class ConfigurationList { positional = ['./test/*.{js,cjs,mjs}']; } - this.patterns = positional.map(f => { + this.patterns = positional.map((f) => { if (path.isAbsolute(f)) { return { glob: false, value: path.normalize(f) }; } else { diff --git a/src/constants.ts b/src/constants.ts index f60d224..7d75cf5 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -13,6 +13,7 @@ export const defaultTestSymbols: ITestSymbols = { suite: ['describe', 'suite'], test: ['it', 'test'], extractWith: 'evaluation', + extractTimeout: 10_000, }; export const showConfigErrorCommand = 'mocha-vscode.showConfigError'; @@ -25,12 +26,17 @@ function equalsIgnoreCase(a: string, b: string) { 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'); + 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 "); -} \ No newline at end of file + return equalsIgnoreCase(ext, '.mjs') || code.includes('import ') || code.includes('export '); +} diff --git a/src/controller.ts b/src/controller.ts index bc09e5d..70982c0 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -60,6 +60,7 @@ export class Controller { } constructor( + private readonly logChannel: vscode.LogOutputChannel, public readonly ctrl: vscode.TestController, private readonly wf: vscode.WorkspaceFolder, private readonly smStore: SourceMapStore, @@ -110,7 +111,7 @@ export class Controller { let tree: IParsedNode[]; try { - tree = await extract(uri.fsPath, contents, this.extractMode.value); + tree = await extract(this.logChannel, uri.fsPath, contents, this.extractMode.value); } catch (e) { this.deleteFileTests(uri.toString()); return; @@ -175,8 +176,7 @@ export class Controller { node.endLine !== undefined && node.endColumn !== undefined ? sourceMap.originalPositionFor(node.endLine, node.endColumn) : start; - const file = last(this.getContainingItemsForFile(start.uri, { compiledFile: uri }))! - .item!; + const file = last(this.getContainingItemsForFile(start.uri, { compiledFile: uri }))!.item!; diagnosticCollection.delete(start.uri); newTestsInFile.set(node.name, add(file, node, start, end)); } @@ -280,14 +280,14 @@ export class Controller { if (prev) { this.runProfiles.set(name, prev); oldRunHandlers.delete(name); - return + return; } 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.ctrl.createRunProfile(name, vscode.TestRunProfileKind.Debug, debug, true), ]; this.runProfiles.set(name, profiles); diff --git a/src/errors.ts b/src/errors.ts index 7655e17..6e826bb 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------*/ /** Errors with a human-readable message. */ -export class HumanError extends Error { } +export class HumanError extends Error {} export class ConfigProcessReadError extends HumanError { constructor(output: string) { diff --git a/src/extension.ts b/src/extension.ts index da92c78..e2abee9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -23,13 +23,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; } @@ -45,8 +49,12 @@ export function activate(context: vscode.ExtensionContext) { await Promise.all( folders.map(async (folder) => { const files = await vscode.workspace.findFiles( - new vscode.RelativePattern(folder, configFilePattern), '**/node_modules/**' + 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( @@ -58,7 +66,7 @@ export function activate(context: vscode.ExtensionContext) { : folder.name, ); - ctrls.push(new Controller(ctrl, folder, smStore, file, runner)); + ctrls.push(new Controller(logChannel, ctrl, folder, smStore, file, runner)); } }), ); @@ -71,24 +79,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 @@ -105,12 +95,12 @@ 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, ); } -export function deactivate() { } +export function deactivate() {} diff --git a/src/extract/evaluate.ts b/src/extract/evaluate.ts index 3565ccc..49a2056 100644 --- a/src/extract/evaluate.ts +++ b/src/extract/evaluate.ts @@ -5,12 +5,11 @@ import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping'; import * as errorParser from 'error-stack-parser'; -import { - transform as esbuildTransform -} from 'esbuild'; +import { transform as esbuildTransform } from 'esbuild'; import * as vm from 'vm'; import { IParsedNode, ITestSymbols, NodeKind } from '.'; import { isEsm, isTypeScript } from '../constants'; +import * as vscode from 'vscode'; /** * Honestly kind of amazed this works. We can use a Proxy as our globalThis @@ -25,7 +24,12 @@ import { isEsm, isTypeScript } from '../constants'; * is also effective in stubbing require() so we know code is nicely isolated. */ -export async function extractWithEvaluation(filePath: string, 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 @@ -54,7 +58,11 @@ export async function extractWithEvaluation(filePath: string, code: string, symb set: () => true, }); - function makeTesterFunction(kind: NodeKind, sourceMap?: TraceMap | undefined, 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(); @@ -80,7 +88,7 @@ export async function extractWithEvaluation(filePath: string, code: string, symb try { const startMapped = originalPositionFor(sourceMap, { line: startLine, - column: startColumn + column: startColumn, }); if (startMapped.line !== null) { startLine = startMapped.line + 1; @@ -88,14 +96,13 @@ export async function extractWithEvaluation(filePath: string, code: string, symb } const endMapped = originalPositionFor(sourceMap, { line: endLine, - column: endColumn - }) + column: endColumn, + }); if (endMapped.line !== null) { endLine = endMapped.line + 1; endColumn = endMapped.column; } - } - catch (e) { + } catch (e) { console.error('error mapping source', e); } } @@ -107,7 +114,7 @@ export async function extractWithEvaluation(filePath: string, code: string, symb startColumn, endLine, endColumn, - children: [] + children: [], }; if (directive) { node.directive = directive; @@ -144,16 +151,15 @@ export async function extractWithEvaluation(filePath: string, code: string, symb 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 + 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) { - // TODO[log output]: invalid source map? -> ignore for now + } catch (e) { + logChannel?.error('Error parsing source map of TypeScript output', e); } } @@ -177,11 +183,9 @@ export async function extractWithEvaluation(filePath: string, code: string, symb }, }); - vm.runInNewContext(code, contextObj, { - timeout: 10000 // TODO: setting? + timeout: symbols.extractTimeout, }); return stack[0].children; -}; - +} diff --git a/src/extract/index.ts b/src/extract/index.ts index 9e76263..fd87bd8 100644 --- a/src/extract/index.ts +++ b/src/extract/index.ts @@ -6,13 +6,19 @@ import { extractWithEvaluation } from './evaluate'; import { extractWithAst } from './syntax'; import { ITestSymbols } from './types'; +import * as vscode from 'vscode'; export * from './types'; -export const extract = async (filePath: string, code: string, symbols: ITestSymbols) => { +export const extract = async ( + logChannel: vscode.LogOutputChannel, + filePath: string, + code: string, + symbols: ITestSymbols, +) => { if (symbols.extractWith === 'evaluation') { try { - return await extractWithEvaluation(filePath, code, symbols); + return await extractWithEvaluation(logChannel, filePath, code, symbols); } catch (e) { console.warn('error evaluating, will fallback', e); // fall through diff --git a/src/extract/syntax.ts b/src/extract/syntax.ts index dcfaf54..71b5529 100644 --- a/src/extract/syntax.ts +++ b/src/extract/syntax.ts @@ -27,7 +27,7 @@ export const acornOptions: AcornOptions = { }; export const esTreeOptions: TSESTreeOptions = { - jsDocParsingMode: 'none' + jsDocParsingMode: 'none', }; const getStringish = (nameArg: Node | undefined): string | undefined => { @@ -63,17 +63,18 @@ const traverse = ( visitor.leave(node); }; - 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 ast = isTypeScript(filePath) + ? (esTreeParse(text, esTreeOptions) as Node) + : (acornParse(text, acornOptions) as Node); const interestingName = (name: string) => symbols.suite.includes(name) ? NodeKind.Suite : symbols.test.includes(name) - ? NodeKind.Test - : undefined; + ? NodeKind.Test + : undefined; const stack: { node: Node; r: IParsedNode }[] = []; stack.push({ node: undefined, r: { children: [] } } as any); diff --git a/src/extract/types.ts b/src/extract/types.ts index 870dd8d..b3ac031 100644 --- a/src/extract/types.ts +++ b/src/extract/types.ts @@ -4,24 +4,25 @@ *--------------------------------------------------------*/ 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; + 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'; + suite: readonly string[]; + test: readonly string[]; + extractWith: 'evaluation' | 'syntax'; + extractTimeout: number; } export const enum NodeKind { - Suite, - Test, -} \ No newline at end of file + Suite, + Test, +} diff --git a/src/reporter/fullJsonStreamReporter.ts b/src/reporter/fullJsonStreamReporter.ts index 594bbb8..c6f81c0 100644 --- a/src/reporter/fullJsonStreamReporter.ts +++ b/src/reporter/fullJsonStreamReporter.ts @@ -60,7 +60,7 @@ const clean = (test: Mocha.Test) => { !test.duration || test.duration < test.slow() / 2 ? ('fast' as const) : test.duration > test.slow() - ? ('slow' as const) - : ('medium' as const), + ? ('slow' as const) + : ('medium' as const), }; -}; \ No newline at end of file +}; diff --git a/src/reporter/fullJsonStreamReporterTypes.ts b/src/reporter/fullJsonStreamReporterTypes.ts index 5b8f372..99a2c57 100644 --- a/src/reporter/fullJsonStreamReporterTypes.ts +++ b/src/reporter/fullJsonStreamReporterTypes.ts @@ -4,47 +4,47 @@ *--------------------------------------------------------*/ export enum MochaEvent { - Start = 'start', - TestStart = 'testStart', - Pass = 'pass', - Fail = 'fail', - End = 'end', - SuiteStart = 'suiteStart', + Start = 'start', + TestStart = 'testStart', + Pass = 'pass', + Fail = 'fail', + End = 'end', + SuiteStart = 'suiteStart', } export interface IStartEvent { - total: number; + total: number; } export interface ITestStartEvent { - path: string[]; - currentRetry: number; - file?: string; + path: string[]; + currentRetry: number; + file?: string; } export interface IPassEvent extends ITestStartEvent { - duration?: number; - speed: 'fast' | 'medium' | 'slow'; + duration?: number; + speed: 'fast' | 'medium' | 'slow'; } export interface IFailEvent extends IPassEvent { - err: string; - stack: string | null; - expected?: string; - actual?: string; + err: string; + stack: string | null; + expected?: string; + actual?: string; } -export interface IEndEvent { } +export interface IEndEvent {} export interface ISuiteStartEvent { - path: string[]; - file?: string; + 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]; \ No newline at end of file + | [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 fa48938..cf79209 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -16,6 +16,7 @@ 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 { config: ConfigurationFile; @@ -31,16 +32,19 @@ export type RunHandler = ( export class TestRunner { constructor( + private readonly logChannel: vscode.LogOutputChannel, private readonly smStore: SourceMapStore, private readonly launchConfig: ConfigValue>, - ) { } + ) {} public makeHandler( ctrl: vscode.TestController, config: ConfigurationFile, - debug: boolean + debug: boolean, ): RunHandler { return async (request) => { + this.logChannel.debug('Creating new test run ', request); + const run = ctrl.createTestRun(request); const { args, compiledFileTests, leafTests } = await this.prepareArguments( ctrl, @@ -92,7 +96,8 @@ export class TestRunner { const { path } = parsed[1]; if (path.length > 0) { enqueueLine( - `${' '.repeat(path.length - 1)}${styles.green.open} ✓ ${styles.green.close}${path[path.length - 1] + `${' '.repeat(path.length - 1)}${styles.green.open} ✓ ${styles.green.close}${ + path[path.length - 1] }`, ); } @@ -102,7 +107,8 @@ export class TestRunner { ranAnyTest = true; const { file, path } = parsed[1]; enqueueLine( - `${' '.repeat(path.length - 1)}${styles.green.open} ✓ ${styles.green.close}${path[path.length - 1] + `${' '.repeat(path.length - 1)}${styles.green.open} ✓ ${styles.green.close}${ + path[path.length - 1] }`, ); const test = compiledFileTests.lookup(file, path); @@ -118,7 +124,8 @@ export class TestRunner { const tcase = compiledFileTests.lookup(file, path); enqueueLine( - `${' '.repeat(path.length - 1)}${styles.red.open} x ${path.join(' ')}${styles.red.close + `${' '.repeat(path.length - 1)}${styles.red.open} x ${path.join(' ')}${ + styles.red.close }`, ); const rawErr = stack || err; @@ -177,7 +184,9 @@ export class TestRunner { }; run.appendOutput( - `${styles.inverse.open} > ${styles.inverse.close} ${(await config.getMochaSpawnArgs(spawnOpts.args)).join(' ')}}\r\n`, + `${styles.inverse.open} > ${styles.inverse.close} ${( + await config.getMochaSpawnArgs(spawnOpts.args) + ).join(' ')}}\r\n`, ); try { @@ -187,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)); } @@ -209,6 +220,7 @@ export class TestRunner { } if (!ranAnyTest) { + this.logChannel.debug('No tests ran, show error'); await vscode.commands.executeCommand('testing.showMostRecentOutput'); } @@ -221,13 +233,13 @@ export class TestRunner { 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(); const launchConfig = this.launchConfig.value || {}; - Promise.resolve( vscode.debug.startDebugging(config.wf, { ...launchConfig, @@ -235,10 +247,7 @@ export class TestRunner { request: 'launch', name: `Mocha Test (${config.uri.fsPath})`, program: spawnArgs[1], - args: [ - ...spawnArgs.slice(2), - ...(launchConfig.args || []) - ], + args: [...spawnArgs.slice(2), ...(launchConfig.args || [])], env: { ...launchConfig.env }, __extensionSessionKey: sessionKey, @@ -304,12 +313,23 @@ export class TestRunner { }), ); }).finally(() => { - ds.dispose() + ds.dispose(); }); } private async runWithoutDebug({ args, config, onLine, token }: ISpawnOptions) { - const cli = await config.spawnMocha(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(); } @@ -381,7 +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 + // 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); diff --git a/src/source-map.ts b/src/source-map.ts index 372e0ec..af1ff24 100644 --- a/src/source-map.ts +++ b/src/source-map.ts @@ -35,7 +35,7 @@ const smMappingAccessor = (file: vscode.Uri, sm: TraceMap): IMappingAccessor => } return new vscode.Location(vscode.Uri.parse(source), new vscode.Position(smLine, smCol)); - } + }, }); export const parseSourceMap = ( diff --git a/src/test/integration/simple.test.ts b/src/test/integration/simple.test.ts index bd25903..da9ea53 100644 --- a/src/test/integration/simple.test.ts +++ b/src/test/integration/simple.test.ts @@ -203,4 +203,4 @@ describe('simple', () => { ['hello.test.js', [['math', [['addition'], ['subtraction']]]]], ]); }); -}); \ No newline at end of file +}); diff --git a/src/test/integration/typescript.test.ts b/src/test/integration/typescript.test.ts index 91b09f0..fc500d2 100644 --- a/src/test/integration/typescript.test.ts +++ b/src/test/integration/typescript.test.ts @@ -5,12 +5,7 @@ import { expect } from 'chai'; import * as vscode from 'vscode'; -import { - captureTestRun, - expectTestTree, - getController, - integrationTestPrepare -} from '../util'; +import { captureTestRun, expectTestTree, getController, integrationTestPrepare } from '../util'; describe('typescript', () => { const workspaceFolder = integrationTestPrepare('typescript'); @@ -18,9 +13,7 @@ describe('typescript', () => { it('discovers tests', async () => { const c = await getController(); - await expectTestTree(c, [ - ['hello.test.ts', [['math', [['addition'], ['subtraction']]]]], - ]); + await expectTestTree(c, [['hello.test.ts', [['math', [['addition'], ['subtraction']]]]]]); }); it('runs tests', async () => { @@ -62,4 +55,4 @@ describe('typescript', () => { 'hello.test.ts/math/subtraction': ['enqueued', 'started', 'passed'], }); }); -}); \ No newline at end of file +}); diff --git a/src/test/unit/extract/evaluate.test.ts b/src/test/unit/extract/evaluate.test.ts index 41cd63e..da670f0 100644 --- a/src/test/unit/extract/evaluate.test.ts +++ b/src/test/unit/extract/evaluate.test.ts @@ -11,12 +11,9 @@ import { extractWithEvaluation } from '../../../extract/evaluate'; describe('evaluate', () => { it('extracts basic suite', async () => { const src = await extractWithEvaluation( + undefined, 'test.js', - [ - "suite('hello', () => {", - " it('works', () => {});", - "})" - ].join('\n'), + ["suite('hello', () => {", " it('works', () => {});", '})'].join('\n'), defaultTestSymbols, ); expect(src).to.deep.equal([ @@ -44,13 +41,14 @@ describe('evaluate', () => { it('can evaluate and extract a test table', async () => { const src = await extractWithEvaluation( + undefined, 'test.js', [ "suite('hello', () => {", " for (const name of ['foo', 'bar', 'baz']) {", - " it(name, () => {});", - " }", - "})" + ' it(name, () => {});', + ' }', + '})', ].join('\n'), defaultTestSymbols, ); @@ -96,13 +94,9 @@ describe('evaluate', () => { }); it('handles errors appropriately', async () => { const src = await extractWithEvaluation( + undefined, 'test.js', - [ - "suite('hello', () => {", - " throw new Error('whoops');", - "})" - ].join('\n') - , + ["suite('hello', () => {", " throw new Error('whoops');", '})'].join('\n'), defaultTestSymbols, ); expect(src).to.deep.equal([ @@ -120,14 +114,11 @@ describe('evaluate', () => { }); it('works with skip/only', async () => { const src = await extractWithEvaluation( + undefined, 'test.js', - [ - "suite('hello', () => {", - " it.only('a', ()=>{});", - " it.skip('a', ()=>{});", - "})" - ].join('\n') - , + ["suite('hello', () => {", " it.only('a', ()=>{});", " it.skip('a', ()=>{});", '})'].join( + '\n', + ), defaultTestSymbols, ); expect(src).to.deep.equal([ @@ -166,6 +157,7 @@ describe('evaluate', () => { it('stubs out requires and placeholds correctly', async () => { const src = await extractWithEvaluation( + undefined, 'test.js', `require("some invalid module").doing().other.things()`, defaultTestSymbols, @@ -175,6 +167,7 @@ describe('evaluate', () => { it('runs esbuild-style modules', async () => { const src = await extractWithEvaluation( + undefined, 'test.js', `var foo = () => suite('hello', () => {}); foo();`, defaultTestSymbols, diff --git a/src/test/unit/extract/syntax.test.ts b/src/test/unit/extract/syntax.test.ts index ee02d76..560b973 100644 --- a/src/test/unit/extract/syntax.test.ts +++ b/src/test/unit/extract/syntax.test.ts @@ -12,11 +12,7 @@ describe('syntax', () => { it('extracts basic suite', () => { const src = extractWithAst( 'test.js', - [ - "suite('hello', () => {", - " it('works', () => {});", - "})" - ].join('\n'), + ["suite('hello', () => {", " it('works', () => {});", '})'].join('\n'), defaultTestSymbols, ); expect(src).to.deep.equal([ @@ -45,12 +41,9 @@ describe('syntax', () => { it('works with skip/only', () => { const src = extractWithAst( 'test.js', - [ - "suite('hello', () => {", - " it.only('a', ()=>{});", - " it.skip('a', ()=>{});", - "})" - ].join('\n'), + ["suite('hello', () => {", " it.only('a', ()=>{});", " it.skip('a', ()=>{});", '})'].join( + '\n', + ), defaultTestSymbols, ); expect(src).to.deep.equal([ @@ -93,9 +86,9 @@ describe('syntax', () => { [ "suite('hello', () => {", " for (const name of ['foo', 'bar', 'baz']) {", - " it(name, () => {});", - " }", - "})" + ' it(name, () => {});', + ' }', + '})', ].join('\n'), defaultTestSymbols, ); diff --git a/src/test/util.ts b/src/test/util.ts index 9e01850..4c9ef1b 100644 --- a/src/test/util.ts +++ b/src/test/util.ts @@ -19,7 +19,9 @@ export function integrationTestPrepare(name: 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`); + assert.fail( + `Workspace Folder '${workspaceFolder}' doesn't exist, something is wrong with the test setup`, + ); } beforeEach(async () => { @@ -33,7 +35,6 @@ export function integrationTestPrepare(name: string) { 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); @@ -55,7 +56,6 @@ async function backupWorkspace(source: string) { return backupFolder; } - export async function getController() { const c = await vscode.commands.executeCommand(getControllersForTestCommand); @@ -66,7 +66,7 @@ export async function getController() { const controller = c[0]; await controller.scanFiles(); return controller; -}; +} type TestTreeExpectation = [string, TestTreeExpectation[]?]; @@ -82,7 +82,7 @@ function buildTreeExpectation(entry: TestTreeExpectation, c: vscode.TestItemColl } entry[1]?.sort(([a], [b]) => a.localeCompare(b)); -}; +} export function onceChanged(controller: Controller, timeout: number = 10000) { return new Promise((resolve, reject) => { @@ -98,7 +98,7 @@ export async function expectTestTree({ ctrl }: Controller, tree: TestTreeExpecta 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 */ async function rmrf(path: string) { @@ -112,7 +112,7 @@ async function rmrf(path: string) { } } } -}; +} type TestState = 'enqueued' | 'started' | 'skipped' | 'failed' | 'errored' | 'passed'; @@ -213,4 +213,4 @@ export async function captureTestRun(ctrl: Controller, req: vscode.TestRunReques } finally { createTestRun.restore(); } -}; +} From 8d265a17db6653b036371c271e956c256128fdc3 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 17:44:47 +0200 Subject: [PATCH 16/26] chore: some more logs --- src/controller.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/controller.ts b/src/controller.ts index 70982c0..a6ec0f3 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -94,6 +94,8 @@ export class Controller { await this.readConfig(); } + this.logChannel.debug('Syncing file', uri); + const includeViaConfigs = this.currentConfig?.includesTestFile(uri); if (!includeViaConfigs) { return; @@ -106,6 +108,7 @@ 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; } @@ -113,6 +116,7 @@ export class Controller { try { 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; } From c3d6e1d9764985211304c33a2a560e7b436d0413 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 21:49:27 +0200 Subject: [PATCH 17/26] fix: Line numbers on typescript --- src/controller.ts | 5 + src/extension.ts | 6 +- src/extract/evaluate.ts | 19 +- .../unit/extract/evaluate-typescript.test.ts | 170 ++++++++++++++++++ src/test/unit/extract/evaluate.test.ts | 26 ++- src/test/unit/extract/syntax.test.ts | 20 ++- src/test/util.ts | 4 + test-workspaces/simple/goodbye.test.js | 35 ++++ 8 files changed, 258 insertions(+), 27 deletions(-) create mode 100644 src/test/unit/extract/evaluate-typescript.test.ts diff --git a/src/controller.ts b/src/controller.ts index a6ec0f3..975d9f0 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -67,6 +67,11 @@ export class Controller { 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; diff --git a/src/extension.ts b/src/extension.ts index e2abee9..b65dbff 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,11 +7,7 @@ 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'; diff --git a/src/extract/evaluate.ts b/src/extract/evaluate.ts index 49a2056..e626d54 100644 --- a/src/extract/evaluate.ts +++ b/src/extract/evaluate.ts @@ -73,16 +73,13 @@ export async function extractWithEvaluation( return placeholder(); } - let startLine = (frame.lineNumber || 1) - 1; - let startColumn = (frame.columnNumber || 1) - 1; + let startLine = frame.lineNumber || 1; + let startColumn = frame.columnNumber || 1; // approximate the length of the test case: let functionLines = String(callback).split('\n'); let endLine = startLine + functionLines.length - 1; let endColumn = functionLines[functionLines.length - 1].length; - if (endLine === startLine) { - endColumn = Number.MAX_SAFE_INTEGER; // assume it takes the entire line of a single-line test case - } if (sourceMap) { try { @@ -91,7 +88,7 @@ export async function extractWithEvaluation( column: startColumn, }); if (startMapped.line !== null) { - startLine = startMapped.line + 1; + startLine = startMapped.line; startColumn = startMapped.column; } const endMapped = originalPositionFor(sourceMap, { @@ -99,7 +96,7 @@ export async function extractWithEvaluation( column: endColumn, }); if (endMapped.line !== null) { - endLine = endMapped.line + 1; + endLine = endMapped.line; endColumn = endMapped.column; } } catch (e) { @@ -107,12 +104,16 @@ export async function extractWithEvaluation( } } + if (endLine === startLine) { + endColumn = Number.MAX_SAFE_INTEGER; // assume it takes the entire line of a single-line test case + } + const node: IParsedNode = { name, kind, - startLine, + startLine: startLine - 1, startColumn, - endLine, + endLine: endLine - 1, endColumn, children: [], }; 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..0ae4a60 --- /dev/null +++ b/src/test/unit/extract/evaluate-typescript.test.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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/test/unit/extract/evaluate.test.ts b/src/test/unit/extract/evaluate.test.ts index da670f0..d6a2afa 100644 --- a/src/test/unit/extract/evaluate.test.ts +++ b/src/test/unit/extract/evaluate.test.ts @@ -7,13 +7,18 @@ import { expect } from 'chai'; import { defaultTestSymbols } from '../../../constants'; import { NodeKind } from '../../../extract'; import { extractWithEvaluation } from '../../../extract/evaluate'; +import { source } from '../../util'; describe('evaluate', () => { it('extracts basic suite', async () => { const src = await extractWithEvaluation( undefined, 'test.js', - ["suite('hello', () => {", " it('works', () => {});", '})'].join('\n'), + source( + "suite('hello', () => {", // + " it('works', () => {});", + '})', + ), defaultTestSymbols, ); expect(src).to.deep.equal([ @@ -43,13 +48,13 @@ describe('evaluate', () => { const src = await extractWithEvaluation( undefined, 'test.js', - [ - "suite('hello', () => {", + source( + "suite('hello', () => {", // " for (const name of ['foo', 'bar', 'baz']) {", ' it(name, () => {});', ' }', '})', - ].join('\n'), + ), defaultTestSymbols, ); expect(src).to.deep.equal([ @@ -96,7 +101,11 @@ describe('evaluate', () => { const src = await extractWithEvaluation( undefined, 'test.js', - ["suite('hello', () => {", " throw new Error('whoops');", '})'].join('\n'), + source( + "suite('hello', () => {", // + " throw new Error('whoops');", + '})', + ), defaultTestSymbols, ); expect(src).to.deep.equal([ @@ -116,8 +125,11 @@ describe('evaluate', () => { const src = await extractWithEvaluation( undefined, 'test.js', - ["suite('hello', () => {", " it.only('a', ()=>{});", " it.skip('a', ()=>{});", '})'].join( - '\n', + source( + "suite('hello', () => {", // + " it.only('a', ()=>{});", + " it.skip('a', ()=>{});", + '})', ), defaultTestSymbols, ); diff --git a/src/test/unit/extract/syntax.test.ts b/src/test/unit/extract/syntax.test.ts index 560b973..661e138 100644 --- a/src/test/unit/extract/syntax.test.ts +++ b/src/test/unit/extract/syntax.test.ts @@ -7,12 +7,17 @@ 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', - ["suite('hello', () => {", " it('works', () => {});", '})'].join('\n'), + source( + "suite('hello', () => {", // + " it('works', () => {});", + '})', + ), defaultTestSymbols, ); expect(src).to.deep.equal([ @@ -41,8 +46,11 @@ describe('syntax', () => { it('works with skip/only', () => { const src = extractWithAst( 'test.js', - ["suite('hello', () => {", " it.only('a', ()=>{});", " it.skip('a', ()=>{});", '})'].join( - '\n', + source( + "suite('hello', () => {", // + " it.only('a', ()=>{});", + " it.skip('a', ()=>{});", + '})', ), defaultTestSymbols, ); @@ -83,13 +91,13 @@ describe('syntax', () => { it('can detect suite but not dynamic tests', () => { const src = extractWithAst( 'test.js', - [ - "suite('hello', () => {", + source( + "suite('hello', () => {", // " for (const name of ['foo', 'bar', 'baz']) {", ' it(name, () => {});', ' }', '})', - ].join('\n'), + ), defaultTestSymbols, ); expect(src).to.deep.equal([ diff --git a/src/test/util.ts b/src/test/util.ts index 4c9ef1b..7408707 100644 --- a/src/test/util.ts +++ b/src/test/util.ts @@ -14,6 +14,10 @@ import * as vscode from 'vscode'; import { getControllersForTestCommand } from '../constants'; import type { Controller } from '../controller'; +export function source(...lines: string[]) { + return lines.join('\n'); +} + export function integrationTestPrepare(name: string) { let workspaceBackup: string; diff --git a/test-workspaces/simple/goodbye.test.js b/test-workspaces/simple/goodbye.test.js index fcaa770..6c98d25 100644 --- a/test-workspaces/simple/goodbye.test.js +++ b/test-workspaces/simple/goodbye.test.js @@ -1,7 +1,42 @@ const { strictEqual } = require('node:assert'); describe('math', () => { + it('division', async () => { strictEqual(2 / 1, 2); }); + + it('test', ()=>{ }) + + it('test2', ()=>{ }) + + + + + + + + + it('test3', ()=>{ }) + }); + + +describe('xx', async () => { + + + + it('test', ()=>{ }) + + it('test2', ()=>{ }) + + + + + + + + + it('test3', ()=>{ }) + +}) \ No newline at end of file From 87538986f7c5d21360d6053abe0aa4dff5323404 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 21:59:17 +0200 Subject: [PATCH 18/26] chore: Use mocharc as root note text for clarity --- src/extension.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index b65dbff..731a131 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -52,15 +52,7 @@ export function activate(context: vscode.ExtensionContext) { 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, - ); + const ctrl = vscode.tests.createTestController(file.toString(), file.fsPath); ctrls.push(new Controller(logChannel, ctrl, folder, smStore, file, runner)); } From f4cb19f67111000175c1259b386ee85c148ee3b1 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 22:04:35 +0200 Subject: [PATCH 19/26] fix: zero-based columns --- src/extract/evaluate.ts | 6 ++++-- src/test/unit/extract/evaluate.test.ts | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/extract/evaluate.ts b/src/extract/evaluate.ts index e626d54..9a25786 100644 --- a/src/extract/evaluate.ts +++ b/src/extract/evaluate.ts @@ -106,15 +106,17 @@ export async function extractWithEvaluation( 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: startLine - 1, - startColumn, + startColumn: startColumn - 1, endLine: endLine - 1, - endColumn, + endColumn: endColumn, children: [], }; if (directive) { diff --git a/src/test/unit/extract/evaluate.test.ts b/src/test/unit/extract/evaluate.test.ts index d6a2afa..233eb0d 100644 --- a/src/test/unit/extract/evaluate.test.ts +++ b/src/test/unit/extract/evaluate.test.ts @@ -27,7 +27,7 @@ describe('evaluate', () => { kind: NodeKind.Suite, startLine: 0, startColumn: 0, - endColumn: 1, + endColumn: 0, endLine: 2, children: [ { @@ -63,7 +63,7 @@ describe('evaluate', () => { kind: NodeKind.Suite, startLine: 0, startColumn: 0, - endColumn: 1, + endColumn: 0, endLine: 4, children: [ { @@ -115,7 +115,7 @@ describe('evaluate', () => { startLine: 0, startColumn: 0, endLine: 2, - endColumn: 1, + endColumn: 0, children: [], error: 'whoops', }, @@ -140,7 +140,7 @@ describe('evaluate', () => { startLine: 0, startColumn: 0, endLine: 3, - endColumn: 1, + endColumn: 0, children: [ { name: 'a', From 0aa66d76b610bd95d4c192f5b8024efdec48ed16 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 22:12:48 +0200 Subject: [PATCH 20/26] fix: handle inconsistencies in source-map lib --- src/extract/evaluate.ts | 8 +++++--- src/test/unit/extract/evaluate.test.ts | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/extract/evaluate.ts b/src/extract/evaluate.ts index 9a25786..2c803c1 100644 --- a/src/extract/evaluate.ts +++ b/src/extract/evaluate.ts @@ -73,13 +73,15 @@ export async function extractWithEvaluation( return placeholder(); } + // + // 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: let functionLines = String(callback).split('\n'); let endLine = startLine + functionLines.length - 1; - let endColumn = functionLines[functionLines.length - 1].length; + let endColumn = functionLines[functionLines.length - 1].length + 1; if (sourceMap) { try { @@ -89,7 +91,7 @@ export async function extractWithEvaluation( }); if (startMapped.line !== null) { startLine = startMapped.line; - startColumn = startMapped.column; + startColumn = startMapped.column + 1; // columns are 0-based in trace-mapping lib } const endMapped = originalPositionFor(sourceMap, { line: endLine, @@ -97,7 +99,7 @@ export async function extractWithEvaluation( }); if (endMapped.line !== null) { endLine = endMapped.line; - endColumn = endMapped.column; + endColumn = endMapped.column + 1; // columns are 0-based in trace-mapping lib } } catch (e) { console.error('error mapping source', e); diff --git a/src/test/unit/extract/evaluate.test.ts b/src/test/unit/extract/evaluate.test.ts index 233eb0d..d6a2afa 100644 --- a/src/test/unit/extract/evaluate.test.ts +++ b/src/test/unit/extract/evaluate.test.ts @@ -27,7 +27,7 @@ describe('evaluate', () => { kind: NodeKind.Suite, startLine: 0, startColumn: 0, - endColumn: 0, + endColumn: 1, endLine: 2, children: [ { @@ -63,7 +63,7 @@ describe('evaluate', () => { kind: NodeKind.Suite, startLine: 0, startColumn: 0, - endColumn: 0, + endColumn: 1, endLine: 4, children: [ { @@ -115,7 +115,7 @@ describe('evaluate', () => { startLine: 0, startColumn: 0, endLine: 2, - endColumn: 0, + endColumn: 1, children: [], error: 'whoops', }, @@ -140,7 +140,7 @@ describe('evaluate', () => { startLine: 0, startColumn: 0, endLine: 3, - endColumn: 0, + endColumn: 1, children: [ { name: 'a', From 9c25be698920e1d4420a0cede24eb1edada477d6 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 22:26:57 +0200 Subject: [PATCH 21/26] fix: clear out test inconsistencies --- .vscode-test.mjs | 1 + src/extract/evaluate.ts | 4 +-- src/source-map.ts | 10 +++++--- test-workspaces/simple/goodbye.test.js | 35 -------------------------- 4 files changed, 9 insertions(+), 41 deletions(-) diff --git a/.vscode-test.mjs b/.vscode-test.mjs index 99a08a4..b8caac9 100644 --- a/.vscode-test.mjs +++ b/.vscode-test.mjs @@ -37,6 +37,7 @@ let createCommonOptions = (label) => { mocha: { ui: 'bdd', + timeout: 60_000, }, }; } diff --git a/src/extract/evaluate.ts b/src/extract/evaluate.ts index 2c803c1..6a0dcd7 100644 --- a/src/extract/evaluate.ts +++ b/src/extract/evaluate.ts @@ -87,7 +87,7 @@ export async function extractWithEvaluation( try { const startMapped = originalPositionFor(sourceMap, { line: startLine, - column: startColumn, + column: startColumn - 1, }); if (startMapped.line !== null) { startLine = startMapped.line; @@ -95,7 +95,7 @@ export async function extractWithEvaluation( } const endMapped = originalPositionFor(sourceMap, { line: endLine, - column: endColumn, + column: endColumn - 1, }); if (endMapped.line !== null) { endLine = endMapped.line; diff --git a/src/source-map.ts b/src/source-map.ts index af1ff24..0383b39 100644 --- a/src/source-map.ts +++ b/src/source-map.ts @@ -13,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; @@ -21,16 +21,18 @@ 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, 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, column)); } diff --git a/test-workspaces/simple/goodbye.test.js b/test-workspaces/simple/goodbye.test.js index 6c98d25..fcaa770 100644 --- a/test-workspaces/simple/goodbye.test.js +++ b/test-workspaces/simple/goodbye.test.js @@ -1,42 +1,7 @@ const { strictEqual } = require('node:assert'); describe('math', () => { - it('division', async () => { strictEqual(2 / 1, 2); }); - - it('test', ()=>{ }) - - it('test2', ()=>{ }) - - - - - - - - - it('test3', ()=>{ }) - }); - - -describe('xx', async () => { - - - - it('test', ()=>{ }) - - it('test2', ()=>{ }) - - - - - - - - - it('test3', ()=>{ }) - -}) \ No newline at end of file From c06397688906701665d706db810c6aa292e5ce07 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 22:55:31 +0200 Subject: [PATCH 22/26] fix: handle source mapped javascript --- src/source-map.ts | 2 +- src/test/integration/source-mapped.test.ts | 105 ++++++++++++++++++ src/test/integration/typescript.test.ts | 2 - src/test/util.ts | 26 +++++ test-workspaces/source-mapped/.mocharc.js | 3 + test-workspaces/source-mapped/hello.test.js | 12 ++ .../source-mapped/hello.test.js.map | 1 + test-workspaces/source-mapped/hello.test.ts | 11 ++ test-workspaces/source-mapped/package.json | 5 + test-workspaces/source-mapped/tsconfig.json | 17 +++ 10 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 src/test/integration/source-mapped.test.ts create mode 100644 test-workspaces/source-mapped/.mocharc.js create mode 100644 test-workspaces/source-mapped/hello.test.js create mode 100644 test-workspaces/source-mapped/hello.test.js.map create mode 100644 test-workspaces/source-mapped/hello.test.ts create mode 100644 test-workspaces/source-mapped/package.json create mode 100644 test-workspaces/source-mapped/tsconfig.json diff --git a/src/source-map.ts b/src/source-map.ts index 0383b39..e1f32e5 100644 --- a/src/source-map.ts +++ b/src/source-map.ts @@ -36,7 +36,7 @@ const smMappingAccessor = (file: vscode.Uri, sm: TraceMap): IMappingAccessor => return new vscode.Location(file, new vscode.Position(line, column)); } - return new vscode.Location(vscode.Uri.parse(source), new vscode.Position(smLine, smCol)); + return new vscode.Location(vscode.Uri.parse(source), new vscode.Position(smLine - 1, smCol)); }, }); diff --git a/src/test/integration/source-mapped.test.ts b/src/test/integration/source-mapped.test.ts new file mode 100644 index 0000000..8d34ed9 --- /dev/null +++ b/src/test/integration/source-mapped.test.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------- + * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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 index fc500d2..c8924ba 100644 --- a/src/test/integration/typescript.test.ts +++ b/src/test/integration/typescript.test.ts @@ -8,8 +8,6 @@ import * as vscode from 'vscode'; import { captureTestRun, expectTestTree, getController, integrationTestPrepare } from '../util'; describe('typescript', () => { - const workspaceFolder = integrationTestPrepare('typescript'); - it('discovers tests', async () => { const c = await getController(); diff --git a/src/test/util.ts b/src/test/util.ts index 7408707..e00507a 100644 --- a/src/test/util.ts +++ b/src/test/util.ts @@ -13,6 +13,7 @@ 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 function source(...lines: string[]) { return lines.join('\n'); @@ -72,6 +73,31 @@ export async function getController() { 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[]?]; function buildTreeExpectation(entry: TestTreeExpectation, c: vscode.TestItemCollection) { 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/test-workspaces/source-mapped/hello.test.js b/test-workspaces/source-mapped/hello.test.js new file mode 100644 index 0000000..d8514e4 --- /dev/null +++ b/test-workspaces/source-mapped/hello.test.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const node_assert_1 = require("node:assert"); +describe('math', () => { + it('addition', async () => { + (0, node_assert_1.strictEqual)(1 + 1, 2); + }); + it(`subtraction`, async () => { + (0, node_assert_1.strictEqual)(1 - 1, 0); + }); +}); +//# sourceMappingURL=hello.test.js.map \ No newline at end of file diff --git a/test-workspaces/source-mapped/hello.test.js.map b/test-workspaces/source-mapped/hello.test.js.map new file mode 100644 index 0000000..e980d88 --- /dev/null +++ b/test-workspaces/source-mapped/hello.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"hello.test.js","sourceRoot":"","sources":["hello.test.ts"],"names":[],"mappings":";;AAAA,6CAA0C;AAE1C,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;IAEH,EAAE,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;QAC3B,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/test-workspaces/source-mapped/hello.test.ts b/test-workspaces/source-mapped/hello.test.ts new file mode 100644 index 0000000..b01f2ba --- /dev/null +++ b/test-workspaces/source-mapped/hello.test.ts @@ -0,0 +1,11 @@ +import { strictEqual } from 'node:assert'; + +describe('math', () => { + it('addition', async () => { + strictEqual(1 + 1, 2); + }); + + it(`subtraction`, async () => { + strictEqual(1 - 1, 0); + }); +}); diff --git a/test-workspaces/source-mapped/package.json b/test-workspaces/source-mapped/package.json new file mode 100644 index 0000000..2e3ec81 --- /dev/null +++ b/test-workspaces/source-mapped/package.json @@ -0,0 +1,5 @@ +{ + "scripts": { + "build": "tsc -p tsconfig.json" + } +} diff --git a/test-workspaces/source-mapped/tsconfig.json b/test-workspaces/source-mapped/tsconfig.json new file mode 100644 index 0000000..68846bc --- /dev/null +++ b/test-workspaces/source-mapped/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2021", + "lib": ["ES2021"], + "sourceMap": true, + "skipLibCheck": true, + "strict": true /* enable all strict type-checking options */, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + }, + "include": ["."] +} From 602308d91393350c312dbcfc2057929e5a99d13c Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 31 Mar 2024 23:49:43 +0200 Subject: [PATCH 23/26] feat: add support for ignore patterns --- src/configurationFile.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/configurationFile.ts b/src/configurationFile.ts index fa02dfd..81df66a 100644 --- a/src/configurationFile.ts +++ b/src/configurationFile.ts @@ -24,6 +24,7 @@ type ConfigModule = { export type IResolvedConfiguration = Mocha.MochaOptions & { _: string[] | undefined; 'node-option': string[] | undefined; + ignore: string[] | undefined; }; export class ConfigurationFile implements vscode.Disposable { @@ -215,6 +216,25 @@ export class ConfigurationList { }; } }); + + 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), + ), + }; + } + }), + ); + } } /** From 93d84ec60536f94828eecd0d38522e2de2a59c9e Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Tue, 2 Apr 2024 13:58:38 +0200 Subject: [PATCH 24/26] Remove OpenJS foundation for now. --- LICENSE | 2 +- README.md | 3 +-- package.json | 2 +- src/configValue.ts | 2 +- src/configurationFile.ts | 2 +- src/constants.ts | 2 +- src/controller.ts | 2 +- src/disposable.ts | 2 +- src/errors.ts | 2 +- src/extension.ts | 2 +- src/extract/evaluate.ts | 2 +- src/extract/index.ts | 2 +- src/extract/syntax.ts | 2 +- src/extract/types.ts | 2 +- src/iterable.ts | 2 +- src/metadata.ts | 2 +- src/outputQueue.ts | 2 +- src/reporter/fullJsonStreamReporter.ts | 2 +- src/reporter/fullJsonStreamReporterTypes.ts | 2 +- src/runner.ts | 2 +- src/source-map-store.ts | 2 +- src/source-map.ts | 2 +- src/test/integration/simple.test.ts | 2 +- src/test/integration/source-mapped.test.ts | 2 +- src/test/integration/typescript.test.ts | 2 +- src/test/unit/extract/evaluate-typescript.test.ts | 2 +- src/test/unit/extract/evaluate.test.ts | 2 +- src/test/unit/extract/syntax.test.ts | 2 +- src/test/util.ts | 2 +- src/typings/acorn-loose.d.ts | 2 +- 30 files changed, 30 insertions(+), 31 deletions(-) diff --git a/LICENSE b/LICENSE index f9a80c2..b8e5a1c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License - Copyright (c) OpenJS Foundation and contributors, https://openjsf.org + 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 1e1de4b..77e2f3b 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ This is the Mocha extension for VS Code enabling developers to run and debug tes ## 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. +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 diff --git a/package.json b/package.json index ba7d899..e7a192e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "mocha-vscode", "displayName": "Mocha Test Runner", "description": "Run and debug Mocha tests right within VS Code.", - "publisher": "mocha", + "publisher": "danielku15", "version": "1.0.0", "icon": "icon.png", "engines": { diff --git a/src/configValue.ts b/src/configValue.ts index 0ba5952..7d1578b 100644 --- a/src/configValue.ts +++ b/src/configValue.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/configurationFile.ts b/src/configurationFile.ts index 81df66a..557bc76 100644 --- a/src/configurationFile.ts +++ b/src/configurationFile.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/constants.ts b/src/constants.ts index 7d75cf5..301da2e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/controller.ts b/src/controller.ts index 975d9f0..a45ed7f 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/disposable.ts b/src/disposable.ts index e87bb3a..87d2eb7 100644 --- a/src/disposable.ts +++ b/src/disposable.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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 6e826bb..5259e9d 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/extension.ts b/src/extension.ts index 731a131..5e30ec3 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/extract/evaluate.ts b/src/extract/evaluate.ts index 6a0dcd7..ba4db2d 100644 --- a/src/extract/evaluate.ts +++ b/src/extract/evaluate.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/extract/index.ts b/src/extract/index.ts index fd87bd8..3567495 100644 --- a/src/extract/index.ts +++ b/src/extract/index.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/extract/syntax.ts b/src/extract/syntax.ts index 71b5529..5f045d5 100644 --- a/src/extract/syntax.ts +++ b/src/extract/syntax.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/extract/types.ts b/src/extract/types.ts index b3ac031..0299527 100644 --- a/src/extract/types.ts +++ b/src/extract/types.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/iterable.ts b/src/iterable.ts index fcedb8e..701f8cd 100644 --- a/src/iterable.ts +++ b/src/iterable.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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 6108c2f..bbe3905 100644 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/outputQueue.ts b/src/outputQueue.ts index e3efea2..c3792c3 100644 --- a/src/outputQueue.ts +++ b/src/outputQueue.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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 index c6f81c0..733ff63 100644 --- a/src/reporter/fullJsonStreamReporter.ts +++ b/src/reporter/fullJsonStreamReporter.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/reporter/fullJsonStreamReporterTypes.ts b/src/reporter/fullJsonStreamReporterTypes.ts index 99a2c57..bc79567 100644 --- a/src/reporter/fullJsonStreamReporterTypes.ts +++ b/src/reporter/fullJsonStreamReporterTypes.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/runner.ts b/src/runner.ts index cf79209..769197e 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/source-map-store.ts b/src/source-map-store.ts index 42a1b71..4277427 100644 --- a/src/source-map-store.ts +++ b/src/source-map-store.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * 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 e1f32e5..5ebf112 100644 --- a/src/source-map.ts +++ b/src/source-map.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/test/integration/simple.test.ts b/src/test/integration/simple.test.ts index da9ea53..8d084fd 100644 --- a/src/test/integration/simple.test.ts +++ b/src/test/integration/simple.test.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/test/integration/source-mapped.test.ts b/src/test/integration/source-mapped.test.ts index 8d34ed9..4ab0008 100644 --- a/src/test/integration/source-mapped.test.ts +++ b/src/test/integration/source-mapped.test.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/test/integration/typescript.test.ts b/src/test/integration/typescript.test.ts index c8924ba..58810fd 100644 --- a/src/test/integration/typescript.test.ts +++ b/src/test/integration/typescript.test.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/test/unit/extract/evaluate-typescript.test.ts b/src/test/unit/extract/evaluate-typescript.test.ts index 0ae4a60..24df63d 100644 --- a/src/test/unit/extract/evaluate-typescript.test.ts +++ b/src/test/unit/extract/evaluate-typescript.test.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/test/unit/extract/evaluate.test.ts b/src/test/unit/extract/evaluate.test.ts index d6a2afa..9b9ccc9 100644 --- a/src/test/unit/extract/evaluate.test.ts +++ b/src/test/unit/extract/evaluate.test.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/test/unit/extract/syntax.test.ts b/src/test/unit/extract/syntax.test.ts index 661e138..2a9bd70 100644 --- a/src/test/unit/extract/syntax.test.ts +++ b/src/test/unit/extract/syntax.test.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/test/util.ts b/src/test/util.ts index e00507a..1c5d96e 100644 --- a/src/test/util.ts +++ b/src/test/util.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ diff --git a/src/typings/acorn-loose.d.ts b/src/typings/acorn-loose.d.ts index 1b097b2..c4a8c10 100644 --- a/src/typings/acorn-loose.d.ts +++ b/src/typings/acorn-loose.d.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------- - * Copyright (C) OpenJS Foundation and contributors, https://openjsf.org + * Copyright (C) Daniel Kuschny (Danielku15) and contributors. * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ From 3f1ee6a4ec440530363315c0f9b4768d06204126 Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Tue, 2 Apr 2024 14:12:11 +0200 Subject: [PATCH 25/26] chore: Add note about extension to readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 77e2f3b..29081d4 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,11 @@ 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. +> 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. From 5828c936605e240e5684a8f29b1aaf2e8b5d140c Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Tue, 2 Apr 2024 14:20:06 +0200 Subject: [PATCH 26/26] chore: add link to project. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 29081d4..33a5b4d 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ This is the Mocha extension for VS Code enabling developers to run and debug tes > [!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