diff --git a/.vscode/settings.json b/.vscode/settings.json index ccb5a9f..e67409e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,18 @@ // Place your settings in this file to overwrite default and user settings. { + "mochaExplorer.files": "src/test/**/*test.ts", + "mochaExplorer.require": "ts-node/register", + // "mochaExplorer.files": "out/test/**/*.js", + // "mochaExplorer.require": "source-map-support/register", + // "mochaExplorer.watch": "out/**/*.js", + "mochaExplorer.launcherScript": "node_modules/mocha-explorer-launcher-scripts/vscode-test", + "mochaExplorer.autoload": false, + "mochaExplorer.ipcRole": "server", + "mochaExplorer.ipcTimeout": 10000, + "mochaExplorer.env": { + "VSCODE_VERSION": "insiders", + "ELECTRON_RUN_AS_NODE": null + }, "files.exclude": { "out": false, // set this to true to hide the "out" folder with the compiled JS files "**/*.js": true, diff --git a/package-lock.json b/package-lock.json index c90e6ea..4b00dc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,13 +28,15 @@ "eslint": "^8.55.0", "eslint-config-prettier": "^9.1.0", "glob": "^10.3.10", - "husky": "^8.0.3", + "husky": "^8.0.0", "mocha": "^10.2.0", + "mocha-explorer-launcher-scripts": "^0.4.0", "mocha-junit-reporter": "^2.2.1", "mocha-multi-reporters": "^1.5.1", "prettier": "^3.1.0", "rimraf": "^5.0.5", "sinon": "^17.0.1", + "ts-node": "^10.9.1", "typescript": "^5.3.2" }, "engines": { @@ -50,6 +52,18 @@ "node": ">=0.10.0" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -259,6 +273,31 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -357,6 +396,30 @@ "node": ">= 6" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, "node_modules/@types/command-exists": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@types/command-exists/-/command-exists-1.2.3.tgz", @@ -684,6 +747,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", + "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", + "dev": true, + "engines": { + "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", @@ -758,6 +830,12 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -955,6 +1033,12 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -1956,6 +2040,12 @@ "node": ">=10" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -2074,6 +2164,15 @@ "url": "https://opencollective.com/mochajs" } }, + "node_modules/mocha-explorer-launcher-scripts": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/mocha-explorer-launcher-scripts/-/mocha-explorer-launcher-scripts-0.4.0.tgz", + "integrity": "sha512-cik/K4r+7WlhpzRmaecA5MWBPOgFRqCdZ95Tvbc5HBohj1I8vgRvBSfAIKdLVJes0PooFlrOzn7Alh4lEELSjg==", + "dev": true, + "dependencies": { + "vscode-test-adapter-remoting-util": "^0.13.0" + } + }, "node_modules/mocha-junit-reporter": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz", @@ -2704,6 +2803,18 @@ "node": ">=8" } }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2806,6 +2917,12 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2830,6 +2947,64 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2897,6 +3072,32 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/vscode-test-adapter-api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/vscode-test-adapter-api/-/vscode-test-adapter-api-1.9.0.tgz", + "integrity": "sha512-lltjehUP0J9H3R/HBctjlqeUCwn2t9Lbhj2Y500ib+j5Y4H3hw+hVTzuSsfw16LtxY37knlU39QIlasa7svzOQ==", + "dev": true, + "engines": { + "vscode": "^1.23.0" + } + }, + "node_modules/vscode-test-adapter-remoting-util": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/vscode-test-adapter-remoting-util/-/vscode-test-adapter-remoting-util-0.13.0.tgz", + "integrity": "sha512-7yI+A+v4K24j+X/pJLgIlAGCIY1tOs9B/lBpPXMvukfPSJibMGts5t2BNb2Kh1wLe2tJBOADs4pu5oWnXKPvzQ==", + "dev": true, + "dependencies": { + "split": "^1.0.1", + "tslib": "^2.0.0", + "vscode-test-adapter-api": "^1.9.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3022,6 +3223,15 @@ "node": ">=10" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 9a301cc..49eae71 100644 --- a/package.json +++ b/package.json @@ -247,11 +247,13 @@ "glob": "^10.3.10", "husky": "^8.0.0", "mocha": "^10.2.0", + "mocha-explorer-launcher-scripts": "^0.4.0", "mocha-junit-reporter": "^2.2.1", "mocha-multi-reporters": "^1.5.1", "prettier": "^3.1.0", "rimraf": "^5.0.5", "sinon": "^17.0.1", + "ts-node": "^10.9.1", "typescript": "^5.3.2" }, "dependencies": { diff --git a/src/TestProvider/TestExplorerFeature.ts b/src/TestProvider/TestExplorerFeature.ts index af04499..603363f 100644 --- a/src/TestProvider/TestExplorerFeature.ts +++ b/src/TestProvider/TestExplorerFeature.ts @@ -2,6 +2,7 @@ import { CancellationToken, EventEmitter, ExtensionContext, + FileSystemWatcher, GlobPattern, RelativePattern, TestController, @@ -21,9 +22,9 @@ import { gatherTestItems, testData, } from "./TestCases"; -import path = require("path"); +import * as path from "path"; -class TestExplorerFeature { +export class TestExplorerFeature { private watchingTests = new Map< TestItem | "ALL", TestRunProfile | undefined @@ -54,7 +55,7 @@ class TestExplorerFeature { this.ctrl.resolveHandler = async (item) => { if (!item) { this.subscriptions.push( - ...startWatchingWorkspace(ctrl, fileChangedEmitter), + ...(await startWatchingWorkspace(ctrl, fileChangedEmitter)), ); } }; @@ -282,12 +283,12 @@ async function getFilesAndCommonDirectory( return { files, commonDirectory }; } -function startWatchingWorkspace( +async function startWatchingWorkspace( controller: TestController, fileChangedEmitter: EventEmitter, -) { - return getWorkspaceTestPatterns().map( - ({ workspaceFolder, pattern, exclude }) => { +): Promise { + const fileWatcherPromises = getWorkspaceTestPatterns().map( + async ({ workspaceFolder, pattern, exclude }) => { const watcher = workspace.createFileSystemWatcher(pattern); watcher.onDidCreate(async (uri) => { @@ -307,9 +308,11 @@ function startWatchingWorkspace( deleteFromUri(controller, controller.items, uri), ); - findInitialTests(controller, pattern, exclude); + await findInitialTests(controller, pattern, exclude); return watcher; }, ); + + return Promise.all(fileWatcherPromises); } diff --git a/src/test/suite/codelens.test.ts b/src/test/suite/codelens.test.ts index c181570..058927c 100644 --- a/src/test/suite/codelens.test.ts +++ b/src/test/suite/codelens.test.ts @@ -4,7 +4,7 @@ import * as assert from "assert"; // as well as import your extension to test it import * as vscode from "vscode"; // import * as myExtension from '../../extension'; -import path = require("path"); +import * as path from "path"; import { PhpCodeLensProvider } from "../../CodeLens/PhpCodeLensProvider"; import { PhpunitXmlCodeLensProvider } from "../../CodeLens/PhpunitXmlCodeLensProvider"; import { PhpunitArgBuilder } from "../../PhpunitCommand/PhpunitArgBuilder"; diff --git a/src/test/suite/e2e.test.ts b/src/test/suite/e2e.test.ts index ea23d31..e92a060 100644 --- a/src/test/suite/e2e.test.ts +++ b/src/test/suite/e2e.test.ts @@ -4,7 +4,7 @@ import * as assert from "assert"; // as well as import your extension to test it import * as vscode from "vscode"; // import * as myExtension from '../../extension'; -import path = require("path"); +import * as path from "path"; import { before, beforeEach } from "mocha"; import * as fs from "fs"; import { IMyExtensionApi } from "../../MyExtensionApi"; diff --git a/src/test/suite/extension.test.ts b/src/test/suite/extension.test.ts deleted file mode 100644 index f389bec..0000000 --- a/src/test/suite/extension.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as assert from "assert"; - -// You can import and use all API from the 'vscode' module -// as well as import your extension to test it -import * as vscode from "vscode"; -// import * as myExtension from '../../extension'; -import path = require("path"); - -suite("Extension Test Suite", () => { - vscode.window.showInformationMessage("Start all tests."); - - test("Sample test", () => { - assert.strictEqual([1, 2, 3].indexOf(5), -1); - assert.strictEqual([1, 2, 3].indexOf(0), -1); - }); -}); diff --git a/src/test/suite/testExplorer.test.ts b/src/test/suite/testExplorer.test.ts new file mode 100644 index 0000000..4965d9d --- /dev/null +++ b/src/test/suite/testExplorer.test.ts @@ -0,0 +1,76 @@ +import * as assert from "assert"; +import * as vscode from "vscode"; +import * as path from "path"; +import { TestExplorerFeature } from "../../TestProvider/TestExplorerFeature"; +import * as fs from "fs"; + +suite("Test Explorer Test Suite", () => { + test("Test find all test files and build tree", async () => { + const testController = vscode.tests.createTestController( + "phpunitTestController", + "Phpunit", + ); + const testExplorer = new TestExplorerFeature(testController); + await testController.resolveHandler!(undefined); + + type MockTestItem = { label: string; children: Array }; + async function buildDirectoryTree( + dir: string, + parent: MockTestItem, + ): Promise { + const dirents = await fs.promises.readdir(dir, { withFileTypes: true }); + + for (const dirent of dirents) { + const res = path.resolve(dir, dirent.name); + const testItem = { label: dirent.name, children: [] }; + parent.children.push(testItem); + + if (dirent.isDirectory()) { + await buildDirectoryTree(res, testItem); + } + } + } + + const root = { label: "root", children: [] }; + const tree = await buildDirectoryTree( + path.join(vscode.workspace.workspaceFolders![0].uri.fsPath, "tests"), + root, + ); + + function checkLabelsExistInTestItems( + item: MockTestItem, + testItems: vscode.TestItem[], + ): void { + const matchingTestItem = testItems.find( + (testItem) => testItem.label === item.label, + ); + assert(matchingTestItem, `Label ${item.label} does not exist`); + + for (const child of item.children) { + checkLabelsExistInTestItems( + child, + gatherTestItems(matchingTestItem.children), + ); + } + } + + for (const child of root.children) { + checkLabelsExistInTestItems(child, gatherTestItems(testController.items)); + } + }); +}); + +function gatherTestItems(collection: vscode.TestItemCollection) { + const items: vscode.TestItem[] = []; + collection.forEach((item) => items.push(item)); + + return items; +} + +// beforeEach(async () => { +// testController = vscode.tests.createTestController("phpunitTestController", "Phpunit"); +// testExplorer = new TestExplorerFeature(testController); +// }); +// afterEach(async () => { +// testController.dispose(); +// });