From 41ccdbccc5a86b2d63ddf944deba6fde0f62af77 Mon Sep 17 00:00:00 2001 From: Krisztian Notaisz <61833595+kn-ms@users.noreply.github.com> Date: Thu, 17 Aug 2023 18:00:30 +0200 Subject: [PATCH 1/3] configure prettier - left mostly of the tslint config in place and tried to tweak prettier config to do the less change, but some places it is oppinionated (which I like) so the tslint rules had to be adjusted. - prettier is added as a tslint rule, so it will be checked in prs - format script added to run prettier with `npm run format` - applyed the formatting to the codebas --- .prettierrc.json | 7 + .vscode/launch.json | 3 +- .vscode/settings.json | 19 +- package-lock.json | 147 +++++++++ package.json | 286 +++++++++--------- src/configuration/configurationFactory.ts | 2 +- .../placeholderAwareWorkspaceConfiguration.ts | 24 +- ...honExtensionAwareWorkspaceConfiguration.ts | 9 +- .../vscodeWorkspaceConfiguration.ts | 11 +- src/environmentVariablesLoader.ts | 23 +- src/logging/defaultLogger.ts | 24 +- src/logging/logOutputChannel.ts | 1 - src/logging/logger.ts | 1 - .../outputChannels/noopOutputChannel.ts | 1 - .../outputChannels/vscodeOutputChannel.ts | 5 +- src/main.ts | 8 +- src/processRunner.ts | 37 +-- src/pytest/pytestJunitTestStatesParser.ts | 62 ++-- src/pytest/pytestTestCollectionParser.ts | 117 +++---- src/pytest/pytestTestRunner.ts | 146 ++++----- src/pythonRunner.ts | 28 +- src/pythonTestAdapter.ts | 163 +++++----- src/testRunner.ts | 5 +- src/testplan/testplanJunitTestStatesParser.ts | 29 +- src/testplan/testplanTestCollectionParser.ts | 20 +- src/testplan/testplanTestRunner.ts | 70 ++--- src/unittest/unittestScripts.ts | 2 - src/unittest/unittestSuitParser.ts | 82 ++--- src/unittest/unittestTestRunner.ts | 46 +-- src/utilities/collections.ts | 3 +- src/utilities/fs.ts | 3 +- src/utilities/strings.ts | 5 +- src/utilities/tests.ts | 30 +- test/mocha-runner.ts | 6 +- test/test_samples/pytest/.vscode/launch.json | 2 +- .../test_samples/pytest/.vscode/settings.json | 6 +- .../pytest/test/test_minimal.tavern.yaml | 28 +- .../.vscode/settings.json | 4 +- .../testplan/.vscode/settings.json | 2 +- .../.vscode/settings.json | 4 +- .../bad_env_file/.vscode/settings.json | 4 +- .../empty_configuration/.vscode/settings.json | 3 +- .../.vscode/settings.json | 6 +- .../.vscode/settings.json | 2 +- .../.vscode/settings.json | 2 +- test/tests/environmentParsing.test.ts | 8 +- test/tests/idGenerator.test.ts | 2 +- ...eholderAwareWorkspaceConfiguration.test.ts | 32 +- test/tests/pytestArguments.test.ts | 201 ++++++------ test/tests/pytestGeneral.test.ts | 94 +++--- test/tests/pytestScript.test.ts | 126 ++++---- test/tests/pythonTestAdapter.test.ts | 103 +++---- test/tests/testCancellation.test.ts | 9 +- test/tests/testplanGeneral.test.ts | 66 ++-- test/tests/unittestGeneral.test.ts | 52 ++-- test/tests/unittestSuitParser.test.ts | 94 +++--- test/tests/utilities.test.ts | 14 +- .../vscodeWorkspaceConfiguration.test.ts | 4 +- test/tslint.json | 4 +- test/utils/helpers.ts | 214 +++++++------ test/utils/pytest.ts | 41 ++- test/utils/testConfiguration.ts | 1 - test/vscode-runner.ts | 55 ++-- tslint.json | 44 +-- 64 files changed, 1345 insertions(+), 1307 deletions(-) create mode 100644 .prettierrc.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..4a51a2b --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "printWidth": 120, + "tabWidth": 4, + "semi": true, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 3075308..5c040d1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,8 @@ "name": "Run extension", "runtimeExecutable": "${execPath}", "args": ["--extensionDevelopmentPath=${workspaceFolder}"], - "outFiles": ["${workspaceFolder}/out"], + "sourceMaps": true, + "outFiles": ["${workspaceFolder}/out/**/*.js"], "preLaunchTask": "npm: build", "env": { "SOME_PROCESS_VARIABLE": "HelloFromProcessEnv" diff --git a/.vscode/settings.json b/.vscode/settings.json index 030caac..1122f6b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,14 @@ { - "typescript.preferences.quoteStyle": "single", - "javascript.preferences.quoteStyle": "single", - "typescript.implementationsCodeLens.enabled": true, - "tslint.enable": true, - "tslint.alwaysShowStatus": true, - "tslint.autoFixOnSave": true, - "editor.rulers": [120] + "typescript.preferences.quoteStyle": "single", + "javascript.preferences.quoteStyle": "single", + "typescript.implementationsCodeLens.enabled": true, + "tslint.enable": true, + "tslint.alwaysShowStatus": true, + "tslint.autoFixOnSave": true, + "editor.rulers": [120], + "editor.defaultFormatter": "esbenp.prettier-vscode", + "typescript.format.enable": true, + "prettier.enable": true, + "prettier.prettierPath": "./node_modules/prettier", + "prettier.requireConfig": true } diff --git a/package-lock.json b/package-lock.json index 06653e3..1dcf619 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,8 +36,11 @@ "cross-env": "^7.0.3", "glob": "^7.2.0", "mocha": "^9.2.2", + "prettier": "^2.8.8", "rimraf": "^3.0.2", "tslint": "^6.1.3", + "tslint-config-prettier": "^1.18.0", + "tslint-plugin-prettier": "^2.3.0", "typescript": "^4.6.3", "vscode-test": "^1.6.1" }, @@ -1209,6 +1212,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint-plugin-prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz", + "integrity": "sha512-CStQYJgALoQBw3FsBzH0VOVDRnJ/ZimUlpLm226U8qgqYJfPOY/CPK6wyRInMxh73HSKg5wyRwdS4BVYYHwokA==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.1", + "jest-docblock": "^21.0.0" + }, + "engines": { + "node": ">=4.0.0" + }, + "peerDependencies": { + "prettier": ">= 0.11.0" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -1232,6 +1251,12 @@ "node": ">=6" } }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -1683,6 +1708,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "node_modules/jest-docblock": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz", + "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==", + "dev": true + }, "node_modules/js-base64": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz", @@ -1732,6 +1763,12 @@ "node": ">=6" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/linkify-it": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", @@ -2210,6 +2247,21 @@ "node": ">=10" } }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "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", @@ -2736,6 +2788,42 @@ "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" } }, + "node_modules/tslint-config-prettier": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz", + "integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==", + "dev": true, + "bin": { + "tslint-config-prettier-check": "bin/check.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/tslint-plugin-prettier": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslint-plugin-prettier/-/tslint-plugin-prettier-2.3.0.tgz", + "integrity": "sha512-F9e4K03yc9xuvv+A0v1EmjcnDwpz8SpCD8HzqSDe0eyg34cBinwn9JjmnnRrNAs4HdleRQj7qijp+P/JTxt4vA==", + "dev": true, + "dependencies": { + "eslint-plugin-prettier": "^2.2.0", + "lines-and-columns": "^1.1.6", + "tslib": "^1.7.1" + }, + "engines": { + "node": ">= 4" + }, + "peerDependencies": { + "prettier": "^1.9.0 || ^2.0.0", + "tslint": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/tslint-plugin-prettier/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/tslint/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -4079,6 +4167,16 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, + "eslint-plugin-prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz", + "integrity": "sha512-CStQYJgALoQBw3FsBzH0VOVDRnJ/ZimUlpLm226U8qgqYJfPOY/CPK6wyRInMxh73HSKg5wyRwdS4BVYYHwokA==", + "dev": true, + "requires": { + "fast-diff": "^1.1.1", + "jest-docblock": "^21.0.0" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -4092,6 +4190,12 @@ "dev": true, "optional": true }, + "fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -4421,6 +4525,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "jest-docblock": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz", + "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==", + "dev": true + }, "js-base64": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz", @@ -4463,6 +4573,12 @@ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "linkify-it": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", @@ -4828,6 +4944,12 @@ "tunnel-agent": "^0.6.0" } }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -5322,6 +5444,31 @@ } } }, + "tslint-config-prettier": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz", + "integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==", + "dev": true + }, + "tslint-plugin-prettier": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslint-plugin-prettier/-/tslint-plugin-prettier-2.3.0.tgz", + "integrity": "sha512-F9e4K03yc9xuvv+A0v1EmjcnDwpz8SpCD8HzqSDe0eyg34cBinwn9JjmnnRrNAs4HdleRQj7qijp+P/JTxt4vA==", + "dev": true, + "requires": { + "eslint-plugin-prettier": "^2.2.0", + "lines-and-columns": "^1.1.6", + "tslib": "^1.7.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, "tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", diff --git a/package.json b/package.json index 850c076..56b9e3b 100644 --- a/package.json +++ b/package.json @@ -1,147 +1,151 @@ { - "name": "vscode-python-test-adapter", - "displayName": "Python Test Explorer for Visual Studio Code", - "description": "Run your Python tests in the Sidebar of Visual Studio Code", - "icon": "img/icon.png", - "author": "Nikolay Kondratyev", - "publisher": "littlefoxteam", - "version": "0.7.1", - "license": "MIT", - "homepage": "https://github.com/kondratyev-nv/vscode-python-test-adapter", - "repository": { - "type": "git", - "url": "https://github.com/kondratyev-nv/vscode-python-test-adapter.git" - }, - "bugs": { - "url": "https://github.com/kondratyev-nv/vscode-python-test-adapter/issues" - }, - "badges": [ - { - "url": "https://github.com/kondratyev-nv/vscode-python-test-adapter/workflows/GitHub%20Actions%20CI/badge.svg", - "href": "https://github.com/kondratyev-nv/vscode-python-test-adapter/actions?query=workflow%3A%22GitHub+Actions+CI%22+branch%3Amaster", - "description": "Continuous integration (Travis)" + "name": "vscode-python-test-adapter", + "displayName": "Python Test Explorer for Visual Studio Code", + "description": "Run your Python tests in the Sidebar of Visual Studio Code", + "icon": "img/icon.png", + "author": "Nikolay Kondratyev", + "publisher": "littlefoxteam", + "version": "0.7.1", + "license": "MIT", + "homepage": "https://github.com/kondratyev-nv/vscode-python-test-adapter", + "repository": { + "type": "git", + "url": "https://github.com/kondratyev-nv/vscode-python-test-adapter.git" }, - { - "url": "https://dev.azure.com/kondratyev-nv/Python%20Test%20Explorer%20for%20Visual%20Studio%20Code/_apis/build/status/Python%20Test%20Explorer%20for%20Visual%20Studio%20Code%20CI?branchName=master", - "href": "https://dev.azure.com/kondratyev-nv/Python%20Test%20Explorer%20for%20Visual%20Studio%20Code/_build/latest?definitionId=1?branchName=master", - "description": "Continuous integration (Azure Pipelines)" - } - ], - "categories": [ - "Other" - ], - "keywords": [ - "python", - "test", - "testing", - "unittest", - "pytest" - ], - "scripts": { - "clean": "rimraf out *.vsix **/*.pyc **/__pycache__ **/.pytest_cache **/.some_venv **/.venv", - "build": "tsc", - "postbuild": "copyfiles \"resources/python/**/*.py\" out", - "watch": "tsc -w", - "lint": "tslint --project tsconfig.json", - "pretest": "npm run build", - "test": "cross-env SOME_PROCESS_VARIABLE=HelloFromProcessEnv cross-env CODE_TESTS_WORKSPACE=./test/test_samples/samples-workspace.code-workspace node ./out/test/vscode-runner.js", - "prepackage": "npm run build", - "package": "vsce package" - }, - "extensionDependencies": [ - "ms-python.python", - "hbenl.vscode-test-explorer" - ], - "dependencies": { - "argparse": "^2.0.1", - "dotenv": "^16.0.0", - "iconv-lite": "^0.6.3", - "js-base64": "^3.7.2", - "jsonc-parser": "3.0.0", - "tmp": "^0.2.1", - "tslib": "^2.3.1", - "untildify": "^4.0.0", - "vscode-test-adapter-api": "^1.9.0", - "xml2js": "^0.6.2" - }, - "devDependencies": { - "@types/argparse": "^2.0.10", - "@types/chai": "^4.3.0", - "@types/glob": "^7.2.0", - "@types/mocha": "^9.1.0", - "@types/node": "^17.0.23", - "@types/semver": "^7.3.9", - "@types/tmp": "^0.2.3", - "@types/vscode": "^1.59.0", - "@types/xml2js": "^0.4.9", - "chai": "^4.3.6", - "copyfiles": "2.4.1", - "cross-env": "^7.0.3", - "glob": "^7.2.0", - "mocha": "^9.2.2", - "rimraf": "^3.0.2", - "tslint": "^6.1.3", - "typescript": "^4.6.3", - "@vscode/vsce": "^2.15.0", - "vscode-test": "^1.6.1" - }, - "main": "out/src/main.js", - "engines": { - "vscode": "^1.59.0" - }, - "activationEvents": [ - "onLanguage:python", - "onView:test-explorer", - "onCommand:test-explorer.reload", - "onCommand:test-explorer.run-all", - "onCommand:test-explorer.run-file", - "onCommand:test-explorer.run-test-at-cursor", - "onCommand:test-explorer.rerun", - "onCommand:test-explorer.debug-test-at-cursor", - "onCommand:test-explorer.redebug", - "onCommand:test-explorer.cancel", - "onStartupFinished" - ], - "contributes": { - "configuration": { - "properties": { - "pythonTestExplorer.testFramework": { - "type": [ - "string", - "null" - ], - "enum": [ - "unittest", - "pytest", - "testplan", - null - ], - "default": null, - "description": "Test framework to use for Python Test Explorer (default is null and Python extension settings are used)", - "scope": "resource" - }, - "pythonTestExplorer.testplanPath": { - "type": "string", - "default": "test_plan.py", - "description": "Relative path to testplan main suite.", - "scope": "resource" - }, - "pythonTestExplorer.testplanArgs": { - "type": "array", - "description": "Arguments passed in. Each argument is a separate item in the array.", - "default": [], - "items": { - "type": "string" - }, - "scope": "resource" + "bugs": { + "url": "https://github.com/kondratyev-nv/vscode-python-test-adapter/issues" + }, + "badges": [ + { + "url": "https://github.com/kondratyev-nv/vscode-python-test-adapter/workflows/GitHub%20Actions%20CI/badge.svg", + "href": "https://github.com/kondratyev-nv/vscode-python-test-adapter/actions?query=workflow%3A%22GitHub+Actions+CI%22+branch%3Amaster", + "description": "Continuous integration (Travis)" }, - "pythonTestExplorer.testplanEnabled": { - "type": "boolean", - "default": false, - "description": "Enable testing using Testplan. Note that Testplan is only supported for Python 3.7+.", - "scope": "resource" + { + "url": "https://dev.azure.com/kondratyev-nv/Python%20Test%20Explorer%20for%20Visual%20Studio%20Code/_apis/build/status/Python%20Test%20Explorer%20for%20Visual%20Studio%20Code%20CI?branchName=master", + "href": "https://dev.azure.com/kondratyev-nv/Python%20Test%20Explorer%20for%20Visual%20Studio%20Code/_build/latest?definitionId=1?branchName=master", + "description": "Continuous integration (Azure Pipelines)" + } + ], + "categories": [ + "Other" + ], + "keywords": [ + "python", + "test", + "testing", + "unittest", + "pytest" + ], + "scripts": { + "clean": "rimraf out *.vsix **/*.pyc **/__pycache__ **/.pytest_cache **/.some_venv **/.venv", + "build": "tsc", + "postbuild": "copyfiles \"resources/python/**/*.py\" out", + "watch": "tsc -w", + "lint": "tslint --project tsconfig.json", + "format": "prettier --write src test", + "pretest": "npm run build", + "test": "cross-env SOME_PROCESS_VARIABLE=HelloFromProcessEnv cross-env CODE_TESTS_WORKSPACE=./test/test_samples/samples-workspace.code-workspace node ./out/test/vscode-runner.js", + "prepackage": "npm run build", + "package": "vsce package" + }, + "extensionDependencies": [ + "ms-python.python", + "hbenl.vscode-test-explorer" + ], + "dependencies": { + "argparse": "^2.0.1", + "dotenv": "^16.0.0", + "iconv-lite": "^0.6.3", + "js-base64": "^3.7.2", + "jsonc-parser": "3.0.0", + "tmp": "^0.2.1", + "tslib": "^2.3.1", + "untildify": "^4.0.0", + "vscode-test-adapter-api": "^1.9.0", + "xml2js": "^0.6.2" + }, + "devDependencies": { + "@types/argparse": "^2.0.10", + "@types/chai": "^4.3.0", + "@types/glob": "^7.2.0", + "@types/mocha": "^9.1.0", + "@types/node": "^17.0.23", + "@types/semver": "^7.3.9", + "@types/tmp": "^0.2.3", + "@types/vscode": "^1.59.0", + "@types/xml2js": "^0.4.9", + "@vscode/vsce": "^2.15.0", + "chai": "^4.3.6", + "copyfiles": "2.4.1", + "cross-env": "^7.0.3", + "glob": "^7.2.0", + "mocha": "^9.2.2", + "prettier": "^2.8.8", + "rimraf": "^3.0.2", + "tslint": "^6.1.3", + "tslint-config-prettier": "^1.18.0", + "tslint-plugin-prettier": "^2.3.0", + "typescript": "^4.6.3", + "vscode-test": "^1.6.1" + }, + "main": "out/src/main.js", + "engines": { + "vscode": "^1.59.0" + }, + "activationEvents": [ + "onLanguage:python", + "onView:test-explorer", + "onCommand:test-explorer.reload", + "onCommand:test-explorer.run-all", + "onCommand:test-explorer.run-file", + "onCommand:test-explorer.run-test-at-cursor", + "onCommand:test-explorer.rerun", + "onCommand:test-explorer.debug-test-at-cursor", + "onCommand:test-explorer.redebug", + "onCommand:test-explorer.cancel", + "onStartupFinished" + ], + "contributes": { + "configuration": { + "properties": { + "pythonTestExplorer.testFramework": { + "type": [ + "string", + "null" + ], + "enum": [ + "unittest", + "pytest", + "testplan", + null + ], + "default": null, + "description": "Test framework to use for Python Test Explorer (default is null and Python extension settings are used)", + "scope": "resource" + }, + "pythonTestExplorer.testplanPath": { + "type": "string", + "default": "test_plan.py", + "description": "Relative path to testplan main suite.", + "scope": "resource" + }, + "pythonTestExplorer.testplanArgs": { + "type": "array", + "description": "Arguments passed in. Each argument is a separate item in the array.", + "default": [], + "items": { + "type": "string" + }, + "scope": "resource" + }, + "pythonTestExplorer.testplanEnabled": { + "type": "boolean", + "default": false, + "description": "Enable testing using Testplan. Note that Testplan is only supported for Python 3.7+.", + "scope": "resource" + } + } } - } } - } } diff --git a/src/configuration/configurationFactory.ts b/src/configuration/configurationFactory.ts index 86381f3..1c538f8 100644 --- a/src/configuration/configurationFactory.ts +++ b/src/configuration/configurationFactory.ts @@ -11,7 +11,7 @@ export interface IConfigurationFactory { } export class DefaultConfigurationFactory implements IConfigurationFactory { - constructor(private readonly logger: ILogger) { } + constructor(private readonly logger: ILogger) {} public async get(workspaceFolder: WorkspaceFolder): Promise { this.logger.log('info', `Reading configuration for workspace ${workspaceFolder.name}`); diff --git a/src/configuration/placeholderAwareWorkspaceConfiguration.ts b/src/configuration/placeholderAwareWorkspaceConfiguration.ts index 13196dc..77e0eb6 100644 --- a/src/configuration/placeholderAwareWorkspaceConfiguration.ts +++ b/src/configuration/placeholderAwareWorkspaceConfiguration.ts @@ -7,7 +7,7 @@ import { IPytestConfiguration, ITestplanConfiguration, IUnittestConfiguration, - IWorkspaceConfiguration + IWorkspaceConfiguration, } from './workspaceConfiguration'; export class PlaceholderAwareWorkspaceConfiguration implements IWorkspaceConfiguration { @@ -15,7 +15,7 @@ export class PlaceholderAwareWorkspaceConfiguration implements IWorkspaceConfigu private readonly configuration: IWorkspaceConfiguration, public readonly workspaceFolder: WorkspaceFolder, private readonly logger: ILogger - ) { } + ) {} public pythonPath(): string { return this.resolveExecutablePath(this.configuration.pythonPath()); @@ -49,7 +49,7 @@ export class PlaceholderAwareWorkspaceConfiguration implements IWorkspaceConfigu return { pytestPath: () => this.getPytestPath(), isPytestEnabled: original.isPytestEnabled, - pytestArguments: original.pytestArguments.map(argument => this.resolvePlaceholders(argument)), + pytestArguments: original.pytestArguments.map((argument) => this.resolvePlaceholders(argument)), }; } @@ -58,7 +58,7 @@ export class PlaceholderAwareWorkspaceConfiguration implements IWorkspaceConfigu return { testplanPath: () => this.resolveExecutablePath(original.testplanPath()), isTestplanEnabled: original.isTestplanEnabled, - testplanArguments: original.testplanArguments.map(argument => this.resolvePlaceholders(argument)), + testplanArguments: original.testplanArguments.map((argument) => this.resolvePlaceholders(argument)), }; } @@ -74,8 +74,8 @@ export class PlaceholderAwareWorkspaceConfiguration implements IWorkspaceConfigu availableReplacements.set('workspaceRootFolderName', path.basename(this.workspaceFolder.uri.fsPath)); availableReplacements.set('cwd', this.workspaceFolder.uri.fsPath); Object.keys(process.env) - .filter(key => process.env[key]) - .forEach(key => { + .filter((key) => process.env[key]) + .forEach((key) => { availableReplacements.set(`env:${key}`, process.env[key]!); }); @@ -105,9 +105,9 @@ export class PlaceholderAwareWorkspaceConfiguration implements IWorkspaceConfigu private normalizeExecutablePath(originalValue: string): string { const value = untildify(originalValue); if (value.includes(path.posix.sep) || value.includes(path.win32.sep)) { - const absolutePath = path.isAbsolute(value) ? - path.resolve(value) : - path.resolve(this.workspaceFolder.uri.fsPath, value); + const absolutePath = path.isAbsolute(value) + ? path.resolve(value) + : path.resolve(this.workspaceFolder.uri.fsPath, value); return path.normalize(absolutePath); } return value; @@ -121,9 +121,9 @@ export class PlaceholderAwareWorkspaceConfiguration implements IWorkspaceConfigu // see https://github.com/kondratyev-nv/vscode-python-test-adapter/issues/158 private normalizePath(originalValue: string): string { const value = untildify(originalValue); - const absolutePath = path.isAbsolute(value) ? - path.resolve(value) : - path.resolve(this.workspaceFolder.uri.fsPath, value); + const absolutePath = path.isAbsolute(value) + ? path.resolve(value) + : path.resolve(this.workspaceFolder.uri.fsPath, value); return path.normalize(absolutePath); } } diff --git a/src/configuration/pythonExtensionAwareWorkspaceConfiguration.ts b/src/configuration/pythonExtensionAwareWorkspaceConfiguration.ts index e86db37..115c830 100644 --- a/src/configuration/pythonExtensionAwareWorkspaceConfiguration.ts +++ b/src/configuration/pythonExtensionAwareWorkspaceConfiguration.ts @@ -6,7 +6,7 @@ import { IPytestConfiguration, ITestplanConfiguration, IUnittestConfiguration, - IWorkspaceConfiguration + IWorkspaceConfiguration, } from './workspaceConfiguration'; const MS_PYTHON_EXTENSION_ID = 'ms-python.python'; @@ -16,9 +16,7 @@ export class PythonExtensionAwareWorkspaceConfiguration implements IWorkspaceCon private readonly configuration: IWorkspaceConfiguration, public readonly workspaceFolder: WorkspaceFolder, private readonly detectedPythonPath?: string - ) { - - } + ) {} public static async for( configuration: IWorkspaceConfiguration, @@ -37,8 +35,7 @@ export class PythonExtensionAwareWorkspaceConfiguration implements IWorkspaceCon logger: ILogger ): Promise { try { - return await PythonExtensionAwareWorkspaceConfiguration - .tryDetectPythonPath(workspaceFolder, logger); + return await PythonExtensionAwareWorkspaceConfiguration.tryDetectPythonPath(workspaceFolder, logger); } catch (error: any) { logger.log( 'crit', diff --git a/src/configuration/vscodeWorkspaceConfiguration.ts b/src/configuration/vscodeWorkspaceConfiguration.ts index 26d2190..e4276c9 100644 --- a/src/configuration/vscodeWorkspaceConfiguration.ts +++ b/src/configuration/vscodeWorkspaceConfiguration.ts @@ -6,7 +6,7 @@ import { ITestplanConfiguration, IUnittestArguments, IUnittestConfiguration, - IWorkspaceConfiguration + IWorkspaceConfiguration, } from './workspaceConfiguration'; import { firstNotEmpty } from '../utilities/collections'; @@ -16,9 +16,7 @@ export class VscodeWorkspaceConfiguration implements IWorkspaceConfiguration { private readonly pythonConfiguration: WorkspaceConfiguration; private readonly testExplorerConfiguration: WorkspaceConfiguration; - constructor( - public readonly workspaceFolder: WorkspaceFolder - ) { + constructor(public readonly workspaceFolder: WorkspaceFolder) { this.unittestArgumentParser = this.configureUnittestArgumentParser(); this.pythonConfiguration = this.getPythonConfiguration(workspaceFolder); this.testExplorerConfiguration = this.getTestExplorerConfiguration(workspaceFolder); @@ -68,10 +66,11 @@ export class VscodeWorkspaceConfiguration implements IWorkspaceConfiguration { private getConfigurationValueOrDefault( configuration: WorkspaceConfiguration, - keys: string[], defaultValue: T + keys: string[], + defaultValue: T ): T { return firstNotEmpty( - keys.map(key => (() => configuration.get(key))), + keys.map((key) => () => configuration.get(key)), defaultValue ); } diff --git a/src/environmentVariablesLoader.ts b/src/environmentVariablesLoader.ts index 1e38b11..8bdb41b 100644 --- a/src/environmentVariablesLoader.ts +++ b/src/environmentVariablesLoader.ts @@ -1,4 +1,3 @@ - import * as path from 'path'; import { ILogger } from './logging/logger'; @@ -18,7 +17,6 @@ export interface IEnvironmentVariables { } export class EnvironmentVariablesLoader { - public static async load( envFilePath: string, globalEnvironment: IEnvironmentVariables, @@ -50,7 +48,9 @@ export class EnvironmentVariablesLoader { for (const [key, value] of Object.entries(localEnvironment)) { environmentVariables[key] = EnvironmentVariablesLoader.resolveEnvironmentVariableValue( - value || '', environmentVariables, globalEnvironment + value || '', + environmentVariables, + globalEnvironment ); } @@ -60,8 +60,7 @@ export class EnvironmentVariablesLoader { private static parse(content: string, globalEnvironment: IEnvironmentVariables): IEnvironmentVariables { const environmentVariables: IEnvironmentVariables = {}; - content.split(NEWLINE).forEach(line => { - + content.split(NEWLINE).forEach((line) => { const parsedKeyValue = EnvironmentVariablesLoader.parseLine(line); if (!parsedKeyValue) { return; @@ -69,7 +68,9 @@ export class EnvironmentVariablesLoader { const [key, value] = parsedKeyValue; environmentVariables[key] = EnvironmentVariablesLoader.resolveEnvironmentVariableValue( - value, environmentVariables, globalEnvironment + value, + environmentVariables, + globalEnvironment ); }); @@ -90,12 +91,10 @@ export class EnvironmentVariablesLoader { private static normalizeValue(value: string): string { const isDoubleQuoted = isEnclosedIn(value, '"'); - const isSingleQuoted = isEnclosedIn(value, '\''); + const isSingleQuoted = isEnclosedIn(value, "'"); if (isSingleQuoted || isDoubleQuoted) { const valueWithoutQuotes = value.substring(1, value.length - 1); - return isDoubleQuoted ? - valueWithoutQuotes.replace(ESCAPED_NEWLINE_REGEX, NEWLINE) : - valueWithoutQuotes; + return isDoubleQuoted ? valueWithoutQuotes.replace(ESCAPED_NEWLINE_REGEX, NEWLINE) : valueWithoutQuotes; } return value.trim(); } @@ -116,7 +115,9 @@ export class EnvironmentVariablesLoader { return value; } return EnvironmentVariablesLoader.resolveEnvironmentVariableValue( - replacement.replace(/\\\$/g, '$'), localEnvironment, globalEnvironment + replacement.replace(/\\\$/g, '$'), + localEnvironment, + globalEnvironment ); } } diff --git a/src/logging/defaultLogger.ts b/src/logging/defaultLogger.ts index 6c3e2f7..36318b8 100644 --- a/src/logging/defaultLogger.ts +++ b/src/logging/defaultLogger.ts @@ -1,4 +1,3 @@ - import { WorkspaceFolder } from 'vscode'; import { ILogger, LogLevel } from './logger'; @@ -9,15 +8,15 @@ export class DefaultLogger implements ILogger { private readonly output: ILogOutputChannel, private readonly workspaceFolder: WorkspaceFolder, private readonly framework: string - ) { } + ) {} public log(level: LogLevel, message: string): void { try { this.output.write( `${new Date().toISOString()} ` + - `${this.levelCode(level)} ` + - `${this.framework} at '${this.workspaceFolder.name}': ` + - `${message}` + `${this.levelCode(level)} ` + + `${this.framework} at '${this.workspaceFolder.name}': ` + + `${message}` ); } catch { /* do nothing if cannot log */ @@ -26,11 +25,16 @@ export class DefaultLogger implements ILogger { private levelCode(level: LogLevel): string { switch (level) { - case 'crit': return 'CRIT'; - case 'warn': return 'WARN'; - case 'info': return 'INFO'; - case 'debug': return ' DBG'; - default: return '?'; + case 'crit': + return 'CRIT'; + case 'warn': + return 'WARN'; + case 'info': + return 'INFO'; + case 'debug': + return ' DBG'; + default: + return '?'; } } } diff --git a/src/logging/logOutputChannel.ts b/src/logging/logOutputChannel.ts index ab50127..9679262 100644 --- a/src/logging/logOutputChannel.ts +++ b/src/logging/logOutputChannel.ts @@ -1,4 +1,3 @@ - export interface ILogOutputChannel { write(message: string): void; } diff --git a/src/logging/logger.ts b/src/logging/logger.ts index f4edee2..2a7a173 100644 --- a/src/logging/logger.ts +++ b/src/logging/logger.ts @@ -1,4 +1,3 @@ - export type LogLevel = 'info' | 'warn' | 'crit' | 'debug'; export interface ILogger { diff --git a/src/logging/outputChannels/noopOutputChannel.ts b/src/logging/outputChannels/noopOutputChannel.ts index 964595a..200a8d2 100644 --- a/src/logging/outputChannels/noopOutputChannel.ts +++ b/src/logging/outputChannels/noopOutputChannel.ts @@ -1,4 +1,3 @@ - import { ILogOutputChannel } from '../logOutputChannel'; export class NoopOutputChannel implements ILogOutputChannel { diff --git a/src/logging/outputChannels/vscodeOutputChannel.ts b/src/logging/outputChannels/vscodeOutputChannel.ts index 3235586..48689fb 100644 --- a/src/logging/outputChannels/vscodeOutputChannel.ts +++ b/src/logging/outputChannels/vscodeOutputChannel.ts @@ -1,12 +1,9 @@ - import { OutputChannel } from 'vscode'; import { ILogOutputChannel } from '../logOutputChannel'; export class VscodeOutputChannel implements ILogOutputChannel { - constructor(private readonly channel: OutputChannel) { - - } + constructor(private readonly channel: OutputChannel) {} public write(message: string): void { this.channel.appendLine(message); } diff --git a/src/main.ts b/src/main.ts index 79cbfaf..ea81e83 100644 --- a/src/main.ts +++ b/src/main.ts @@ -35,9 +35,9 @@ function registerTestAdapters( const adapters = [ new PythonTestAdapter(wf, unittestRunner, unittestConfigurationFactory, unittestLogger), new PythonTestAdapter(wf, pytestRunner, pytestConfigurationFactory, pytestLogger), - new PythonTestAdapter(wf, testplanRunner, testplantConfigurationFactory, testplanLogger) + new PythonTestAdapter(wf, testplanRunner, testplantConfigurationFactory, testplanLogger), ]; - adapters.forEach(adapter => extension.exports.registerTestAdapter(adapter)); + adapters.forEach((adapter) => extension.exports.registerTestAdapter(adapter)); return adapters; } @@ -74,11 +74,11 @@ export async function activate(context: vscode.ExtensionContext) { } } - const workspaceFolderChangedSubscription = vscode.workspace.onDidChangeWorkspaceFolders(event => { + const workspaceFolderChangedSubscription = vscode.workspace.onDidChangeWorkspaceFolders((event) => { for (const workspaceFolder of event.removed) { const adapters = registeredAdapters.get(workspaceFolder); if (adapters) { - adapters.forEach(adapter => { + adapters.forEach((adapter) => { testExplorerExtension.exports.unregisterTestAdapter(adapter); adapter.dispose(); }); diff --git a/src/processRunner.ts b/src/processRunner.ts index 53133eb..fbcc10c 100644 --- a/src/processRunner.ts +++ b/src/processRunner.ts @@ -10,7 +10,7 @@ export interface IProcessRunConfiguration { export interface IProcessExecution { pid: number; - complete(): Promise<{ exitCode: number, output: string }>; + complete(): Promise<{ exitCode: number; output: string }>; cancel(): void; } @@ -21,33 +21,26 @@ class CommandProcessExecution implements IProcessExecution { private readonly commandProcess: ChildProcess; private readonly acceptedExitCodes: readonly number[]; - constructor( - command: string, - args?: string[], - configuration?: IProcessRunConfiguration - ) { - this.commandProcess = spawn( - command, - args, - { - cwd: configuration?.cwd, - env: { - ...process.env, - ...configuration?.environment, - }, - }); + constructor(command: string, args?: string[], configuration?: IProcessRunConfiguration) { + this.commandProcess = spawn(command, args, { + cwd: configuration?.cwd, + env: { + ...process.env, + ...configuration?.environment, + }, + }); this.pid = this.commandProcess.pid || -1; this.acceptedExitCodes = configuration?.acceptedExitCodes || [0]; } - public async complete(): Promise<{ exitCode: number; output: string; }> { - return new Promise<{ exitCode: number, output: string }>((resolve, reject) => { + public async complete(): Promise<{ exitCode: number; output: string }> { + return new Promise<{ exitCode: number; output: string }>((resolve, reject) => { const stdoutBuffer: Buffer[] = []; const stderrBuffer: Buffer[] = []; - this.commandProcess.stdout!.on('data', chunk => stdoutBuffer.push(chunk)); - this.commandProcess.stderr!.on('data', chunk => stderrBuffer.push(chunk)); + this.commandProcess.stdout!.on('data', (chunk) => stdoutBuffer.push(chunk)); + this.commandProcess.stderr!.on('data', (chunk) => stderrBuffer.push(chunk)); - this.commandProcess.once('close', exitCode => { + this.commandProcess.once('close', (exitCode) => { if (this.exitedWithUnexpectedExitCode(exitCode) && !this.commandProcess.killed) { reject(new Error(`Process exited with code ${exitCode}: ${decode(stderrBuffer)}`)); return; @@ -62,7 +55,7 @@ class CommandProcessExecution implements IProcessExecution { resolve({ exitCode: exitCode || 0, output }); }); - this.commandProcess.once('error', error => { + this.commandProcess.once('error', (error) => { reject(new Error(`Error occurred during process execution: ${error}`)); }); }); diff --git a/src/pytest/pytestJunitTestStatesParser.ts b/src/pytest/pytestJunitTestStatesParser.ts index 29ece82..a330bd9 100644 --- a/src/pytest/pytestJunitTestStatesParser.ts +++ b/src/pytest/pytestJunitTestStatesParser.ts @@ -1,4 +1,3 @@ - import { EOL } from 'os'; import * as path from 'path'; import { TestEvent } from 'vscode-test-adapter-api'; @@ -49,10 +48,7 @@ interface ITestCaseResult { type TestState = 'passed' | 'failed' | 'skipped'; -export async function parseTestStates( - outputXmlFile: string, - cwd: string -): Promise { +export async function parseTestStates(outputXmlFile: string, cwd: string): Promise { const content = await readFile(outputXmlFile); const parseResult = await parseXml(content); return parseTestResults(parseResult, cwd); @@ -74,15 +70,20 @@ function parseTestResults(parserResult: any, cwd: string): TestEvent[] { if (!parserResult) { return []; } - const testSuiteResults: ITestSuiteResult[] = parserResult.testsuites ? - parserResult.testsuites.testsuite : // from pytest 5.1.0, see https://github.com/pytest-dev/pytest/issues/5477 - [parserResult.testsuite]; // before pytest 5.1.0 - return testSuiteResults.map(testSuiteResult => { - if (!Array.isArray(testSuiteResult.testcase)) { - return []; - } - return testSuiteResult.testcase.map(testcase => mapToTestState(testcase, cwd)).filter(x => x).map(x => x!); - }).reduce((r, x) => r.concat(x), []); + const testSuiteResults: ITestSuiteResult[] = parserResult.testsuites + ? parserResult.testsuites.testsuite // from pytest 5.1.0, see https://github.com/pytest-dev/pytest/issues/5477 + : [parserResult.testsuite]; // before pytest 5.1.0 + return testSuiteResults + .map((testSuiteResult) => { + if (!Array.isArray(testSuiteResult.testcase)) { + return []; + } + return testSuiteResult.testcase + .map((testcase) => mapToTestState(testcase, cwd)) + .filter((x) => x) + .map((x) => x!); + }) + .reduce((r, x) => r.concat(x), []); } function mapToTestState(testcase: ITestCaseResult, cwd: string): TestEvent | undefined { @@ -102,7 +103,7 @@ function mapToTestState(testcase: ITestCaseResult, cwd: string): TestEvent | und }; } -function getDecorations(state: TestState, line: string, message: string): { line: number, message: string }[] { +function getDecorations(state: TestState, line: string, message: string): { line: number; message: string }[] { if (state === 'passed') { return []; } @@ -110,10 +111,12 @@ function getDecorations(state: TestState, line: string, message: string): { line return []; } const lineNumber = parseInt(line, 10); - return [{ - line: lineNumber, - message, - }]; + return [ + { + line: lineNumber, + message, + }, + ]; } function getTestState(testcase: ITestCaseResult): [TestState, string, string, number | undefined] { @@ -139,11 +142,11 @@ function extractSystemErr(testcase: ITestCaseResult) { return empty(testcase['system-err']) ? '' : testcase['system-err'].join(EOL); } -function extractErrorMessage(errors: { _: string, $: { message: string } }[]): string { +function extractErrorMessage(errors: { _: string; $: { message: string } }[]): string { if (!errors || !errors.length) { return ''; } - return concatNonEmpty(EOL, ...errors.map(e => concatNonEmpty(EOL, e.$.message, e._))); + return concatNonEmpty(EOL, ...errors.map((e) => concatNonEmpty(EOL, e.$.message, e._))); } function buildTestName(cwd: string, test: ITestCaseDescription): string | undefined { @@ -154,13 +157,20 @@ function buildTestName(cwd: string, test: ITestCaseDescription): string | undefi if (!test.classname) { return `${module}`; } - const testClass = test.classname.split('.').filter(p => p).filter(p => p !== '()').join('.'); + const testClass = test.classname + .split('.') + .filter((p) => p) + .filter((p) => p !== '()') + .join('.'); const { matched, position } = matchModule(testClass, test.file); if (!matched) { return undefined; } - const testClassParts = testClass.substring(position).split('.').filter(p => p); + const testClassParts = testClass + .substring(position) + .split('.') + .filter((p) => p); if (testClassParts.length > 0) { return `${module}::${testClassParts.join('::')}::${test.name}`; } else { @@ -168,7 +178,7 @@ function buildTestName(cwd: string, test: ITestCaseDescription): string | undefi } } -function matchModule(testClass: string, testFile: string): { matched: boolean, position: number } { +function matchModule(testClass: string, testFile: string): { matched: boolean; position: number } { const { matched, position } = matchParentPath(testClass, testFile); if (!matched) { return { matched: false, position: -1 }; @@ -188,14 +198,14 @@ function matchModule(testClass: string, testFile: string): { matched: boolean, p return { matched: false, position: -1 }; } -function matchParentPath(testClass: string, testFile: string): { matched: boolean, position: number } { +function matchParentPath(testClass: string, testFile: string): { matched: boolean; position: number } { const parentPathToMatch = path.parse(testFile).dir; if (!parentPathToMatch) { return { matched: true, position: 0 }; } const testFileParentPath = parentPathToMatch.split(path.sep); let index = 0; - const allClassPartsMatchesPath = testFileParentPath.every(pathPart => { + const allClassPartsMatchesPath = testFileParentPath.every((pathPart) => { if (startsWith(testClass, pathPart + '.', index)) { index += pathPart.length + 1; return true; diff --git a/src/pytest/pytestTestCollectionParser.ts b/src/pytest/pytestTestCollectionParser.ts index be2c4e7..0a01bc8 100644 --- a/src/pytest/pytestTestCollectionParser.ts +++ b/src/pytest/pytestTestCollectionParser.ts @@ -8,8 +8,8 @@ const DISCOVERED_TESTS_START_MARK = '==DISCOVERED TESTS BEGIN=='; const DISCOVERED_TESTS_END_MARK = '==DISCOVERED TESTS END=='; interface IDiscoveryResultJson { - tests: { id: string, line: number }[]; - errors: { file: string, message: number }[]; + tests: { id: string; line: number }[]; + errors: { file: string; message: number }[]; } export function parseTestSuites(content: string, cwd: string): (TestSuiteInfo | TestInfo)[] { @@ -21,40 +21,46 @@ export function parseTestSuites(content: string, cwd: string): (TestSuiteInfo | const discoveredTestsJson = content.substring(from + DISCOVERED_TESTS_START_MARK.length, to); const discoveryResult = JSON.parse(discoveredTestsJson) as IDiscoveryResultJson; const allTests = (discoveryResult.tests || []) - .map(line => ({ ...line, id: line.id.replace(/::\(\)/g, '') })) - .filter(line => line.id) - .map(line => splitModule(line, cwd)) - .filter(line => line) - .map(line => line!); - const suites = Array.from(groupBy(allTests, t => t.modulePath).entries()) - .map(([modulePath, tests]) => ({ - type: 'suite' as 'suite', - id: modulePath, - label: path.basename(modulePath), - file: modulePath, - tooltip: modulePath, - children: toTestSuites( - tests.map(t => ({ - idHead: t.modulePath, - idTail: t.testPath, - line: t.line, - path: modulePath, - })) - ), - })); - const aggregatedErrors = Array.from(groupBy((discoveryResult.errors || []), e => e.file).entries()) - .map(([file, messages]) => ({ + .map((line) => ({ ...line, id: line.id.replace(/::\(\)/g, '') })) + .filter((line) => line.id) + .map((line) => splitModule(line, cwd)) + .filter((line) => line) + .map((line) => line!); + const suites = Array.from(groupBy(allTests, (t) => t.modulePath).entries()).map( + ([modulePath, tests]) => + { + type: 'suite' as 'suite', + id: modulePath, + label: path.basename(modulePath), + file: modulePath, + tooltip: modulePath, + children: toTestSuites( + tests.map((t) => ({ + idHead: t.modulePath, + idTail: t.testPath, + line: t.line, + path: modulePath, + })) + ), + } + ); + const aggregatedErrors = Array.from(groupBy(discoveryResult.errors || [], (e) => e.file).entries()).map( + ([file, messages]) => ({ file: path.resolve(cwd, file), - message: messages.map(e => e.message).join(os.EOL), - })); - const discoveryErrorSuites = aggregatedErrors.map(({ file, message }) => ({ - type: 'test' as 'test', - id: file, - file, - label: `Error in ${path.basename(file)}`, - errored: true, - message, - })); + message: messages.map((e) => e.message).join(os.EOL), + }) + ); + const discoveryErrorSuites = aggregatedErrors.map( + ({ file, message }) => + { + type: 'test' as 'test', + id: file, + file, + label: `Error in ${path.basename(file)}`, + errored: true, + message, + } + ); return suites.concat(discoveryErrorSuites); } @@ -69,7 +75,7 @@ function toTestSuites(tests: ITestCaseSplit[]): (TestSuiteInfo | TestInfo)[] { if (empty(tests)) { return []; } - const testsAndSuites = groupBy(tests, t => t.idTail.includes('::')); + const testsAndSuites = groupBy(tests, (t) => t.idTail.includes('::')); const firstLevelTests: (TestSuiteInfo | TestInfo)[] = toFirstLevelTests(testsAndSuites.get(false)); const suites: (TestSuiteInfo | TestInfo)[] = toSuites(testsAndSuites.get(true)); return firstLevelTests.concat(suites); @@ -79,34 +85,39 @@ function toSuites(suites: ITestCaseSplit[] | undefined): TestSuiteInfo[] { if (!suites) { return []; } - return Array.from(groupBy(suites.map(test => splitTest(test)), group => group.idHead).entries()) - .map(([suite, suiteTests]) => ({ - type: 'suite' as 'suite', - id: suite, - label: suiteTests[0].name, - file: suiteTests[0].path, - children: toTestSuites(suiteTests), - tooltip: suite, - })); + return Array.from( + groupBy( + suites.map((test) => splitTest(test)), + (group) => group.idHead + ).entries() + ).map(([suite, suiteTests]) => ({ + type: 'suite' as 'suite', + id: suite, + label: suiteTests[0].name, + file: suiteTests[0].path, + children: toTestSuites(suiteTests), + tooltip: suite, + })); } function toFirstLevelTests(tests: ITestCaseSplit[] | undefined): (TestSuiteInfo | TestInfo)[] { if (!tests) { return []; } - const testsByParameterized = groupBy(tests, t => t.idTail.includes('[')); + const testsByParameterized = groupBy(tests, (t) => t.idTail.includes('[')); const basicTests: (TestSuiteInfo | TestInfo)[] = (testsByParameterized.get(false) || []).map(toTest); - const parameterizedTestsBySuite = groupBy( - testsByParameterized.get(true) || [], - t => t.idTail.substring(0, t.idTail.indexOf('['))); - const parameterizedSuites: (TestSuiteInfo | TestInfo)[] = Array.from(parameterizedTestsBySuite.entries()) - .map(([baseName, parameterizedTests]) => ({ + const parameterizedTestsBySuite = groupBy(testsByParameterized.get(true) || [], (t) => + t.idTail.substring(0, t.idTail.indexOf('[')) + ); + const parameterizedSuites: (TestSuiteInfo | TestInfo)[] = Array.from(parameterizedTestsBySuite.entries()).map( + ([baseName, parameterizedTests]) => ({ type: 'suite' as 'suite', id: `${parameterizedTests[0].idHead}::${baseName}`, label: baseName, file: parameterizedTests[0].path, children: parameterizedTests.map(toTest), - })); + }) + ); return basicTests.concat(parameterizedSuites); } @@ -133,7 +144,7 @@ function splitTest(test: ITestCaseSplit) { }; } -function splitModule(test: { id: string, line: number }, cwd: string) { +function splitModule(test: { id: string; line: number }, cwd: string) { const separatorIndex = test.id.indexOf('::'); if (separatorIndex < 0) { return null; diff --git a/src/pytest/pytestTestRunner.ts b/src/pytest/pytestTestRunner.ts index f38e5d7..7018389 100644 --- a/src/pytest/pytestTestRunner.ts +++ b/src/pytest/pytestTestRunner.ts @@ -1,8 +1,6 @@ import * as path from 'path'; import * as tmp from 'tmp'; -import { - TestEvent, TestSuiteInfo -} from 'vscode-test-adapter-api'; +import { TestEvent, TestSuiteInfo } from 'vscode-test-adapter-api'; import { ArgumentParser } from 'argparse'; import { IWorkspaceConfiguration } from '../configuration/workspaceConfiguration'; @@ -37,13 +35,9 @@ interface IRunArguments { } export class PytestTestRunner implements ITestRunner { - private readonly testExecutions: Map = new Map(); - constructor( - public readonly adapterId: string, - private readonly logger: ILogger - ) { } + constructor(public readonly adapterId: string, private readonly logger: ILogger) {} public cancel(): void { this.testExecutions.forEach((execution, test) => { @@ -109,7 +103,8 @@ export class PytestTestRunner implements ITestRunner { // See https://docs.pytest.org/en/stable/customize.html#finding-the-rootdir `--rootdir=${config.getCwd()}`, `--junitxml=${file}`, - '--override-ini', 'junit_family=xunit1' + '--override-ini', + 'junit_family=xunit1', ].concat(runArguments.argumentsToPass); this.logger.log('info', `Running pytest with arguments: ${testRunArguments.join(', ')}`); @@ -140,14 +135,11 @@ export class PytestTestRunner implements ITestRunner { } this.logger.log('info', `Running ${pytestPath} as an executable`); - return runProcess( - pytestPath, - args, - { - cwd: config.getCwd(), - environment: env, - acceptedExitCodes: PYTEST_NON_ERROR_EXIT_CODES, - }); + return runProcess(pytestPath, args, { + cwd: config.getCwd(), + environment: env, + acceptedExitCodes: PYTEST_NON_ERROR_EXIT_CODES, + }); } private async loadEnvironmentVariables(config: IWorkspaceConfiguration): Promise { @@ -157,13 +149,14 @@ export class PytestTestRunner implements ITestRunner { config.getCwd(), envFileEnvironment.PYTHONPATH, process.env.PYTHONPATH, - DISCOVERY_OUTPUT_PLUGIN_INFO.PACKAGE_PATH - ].filter(item => item).join(path.delimiter); + DISCOVERY_OUTPUT_PLUGIN_INFO.PACKAGE_PATH, + ] + .filter((item) => item) + .join(path.delimiter); - const updatedPytestPlugins = [ - envFileEnvironment.PYTEST_PLUGINS, - DISCOVERY_OUTPUT_PLUGIN_INFO.MODULE_NAME - ].filter(item => item).join(','); + const updatedPytestPlugins = [envFileEnvironment.PYTEST_PLUGINS, DISCOVERY_OUTPUT_PLUGIN_INFO.MODULE_NAME] + .filter((item) => item) + .join(','); return { ...envFileEnvironment, @@ -175,11 +168,13 @@ export class PytestTestRunner implements ITestRunner { private async getJunitReportPath( cwd: string, runArguments: IRunArguments - ): Promise<{ file: string, cleanupCallback: () => void }> { + ): Promise<{ file: string; cleanupCallback: () => void }> { if (runArguments.junitReportPath) { return Promise.resolve({ file: path.resolve(cwd, runArguments.junitReportPath), - cleanupCallback: () => { /* intentionally empty */ }, + cleanupCallback: () => { + /* intentionally empty */ + }, }); } return await this.createTemporaryFile(); @@ -193,35 +188,21 @@ export class PytestTestRunner implements ITestRunner { private getRunArguments(test: string, rawPytestArguments: string[]): IRunArguments { const argumentParser = this.configureCommonArgumentParser(); - argumentParser.add_argument( - '--setuponly', '--setup-only', - { action: 'store_true' }); - argumentParser.add_argument( - '--setupshow', '--setup-show', - { action: 'store_true' }); - argumentParser.add_argument( - '--setupplan', '--setup-plan', - { action: 'store_true' }); - argumentParser.add_argument( - '--collectonly', '--collect-only', - { action: 'store_true' }); - argumentParser.add_argument( - '--trace', - { dest: 'trace', action: 'store_true' }); + argumentParser.add_argument('--setuponly', '--setup-only', { action: 'store_true' }); + argumentParser.add_argument('--setupshow', '--setup-show', { action: 'store_true' }); + argumentParser.add_argument('--setupplan', '--setup-plan', { action: 'store_true' }); + argumentParser.add_argument('--collectonly', '--collect-only', { action: 'store_true' }); + argumentParser.add_argument('--trace', { dest: 'trace', action: 'store_true' }); // Handle positional arguments (list of tests to run). // We hande them only in 'Run' configuration, because they might be used as filter on discovery stage. - argumentParser.add_argument( - 'tests', - { nargs: '*' }); + argumentParser.add_argument('tests', { nargs: '*' }); const [knownArguments, argumentsToPass] = argumentParser.parse_known_args(rawPytestArguments); return { junitReportPath: (knownArguments as { xmlpath?: string }).xmlpath, argumentsToPass: argumentsToPass.concat( - test !== this.adapterId ? - [test] : - (knownArguments as { tests?: string[] }).tests || [] + test !== this.adapterId ? [test] : (knownArguments as { tests?: string[] }).tests || [] ), }; } @@ -230,53 +211,38 @@ export class PytestTestRunner implements ITestRunner { const argumentParser = new ArgumentParser({ exit_on_error: false, }); - argumentParser.add_argument( - '--rootdir', - { action: 'store', dest: 'rootdir' }); - argumentParser.add_argument( - '-x', '--exitfirst', - { dest: 'maxfail', action: 'store_const', const: 1 }); - argumentParser.add_argument( - '--maxfail', - { dest: 'maxfail', action: 'store', default: 0 }); - argumentParser.add_argument( - '--fixtures', '--funcargs', - { action: 'store_true', dest: 'showfixtures', default: false }); - argumentParser.add_argument( - '--fixtures-per-test', - { action: 'store_true', dest: 'show_fixtures_per_test', default: false }); - argumentParser.add_argument( - '--lf', '--last-failed', - { action: 'store_true', dest: 'lf' }); - argumentParser.add_argument( - '--ff', '--failed-first', - { action: 'store_true', dest: 'failedfirst' }); - argumentParser.add_argument( - '--nf', '--new-first', - { action: 'store_true', dest: 'newfirst' }); - argumentParser.add_argument( - '--cache-show', - { action: 'store_true', dest: 'cacheshow' }); - argumentParser.add_argument( - '--lfnf', '--last-failed-no-failures', - { action: 'store', dest: 'last_failed_no_failures', choices: ['all', 'none'], default: 'all' }); - argumentParser.add_argument( - '--pdb', - { dest: 'usepdb', action: 'store_true' }); - argumentParser.add_argument( - '--pdbcls', - { dest: 'usepdb_cls' }); - argumentParser.add_argument( - '--junitxml', '--junit-xml', - { action: 'store', dest: 'xmlpath' }); - argumentParser.add_argument( - '--junitprefix', '--junit-prefix', - { action: 'store' }); + argumentParser.add_argument('--rootdir', { action: 'store', dest: 'rootdir' }); + argumentParser.add_argument('-x', '--exitfirst', { dest: 'maxfail', action: 'store_const', const: 1 }); + argumentParser.add_argument('--maxfail', { dest: 'maxfail', action: 'store', default: 0 }); + argumentParser.add_argument('--fixtures', '--funcargs', { + action: 'store_true', + dest: 'showfixtures', + default: false, + }); + argumentParser.add_argument('--fixtures-per-test', { + action: 'store_true', + dest: 'show_fixtures_per_test', + default: false, + }); + argumentParser.add_argument('--lf', '--last-failed', { action: 'store_true', dest: 'lf' }); + argumentParser.add_argument('--ff', '--failed-first', { action: 'store_true', dest: 'failedfirst' }); + argumentParser.add_argument('--nf', '--new-first', { action: 'store_true', dest: 'newfirst' }); + argumentParser.add_argument('--cache-show', { action: 'store_true', dest: 'cacheshow' }); + argumentParser.add_argument('--lfnf', '--last-failed-no-failures', { + action: 'store', + dest: 'last_failed_no_failures', + choices: ['all', 'none'], + default: 'all', + }); + argumentParser.add_argument('--pdb', { dest: 'usepdb', action: 'store_true' }); + argumentParser.add_argument('--pdbcls', { dest: 'usepdb_cls' }); + argumentParser.add_argument('--junitxml', '--junit-xml', { action: 'store', dest: 'xmlpath' }); + argumentParser.add_argument('--junitprefix', '--junit-prefix', { action: 'store' }); return argumentParser; } - private async createTemporaryFile(): Promise<{ file: string, cleanupCallback: () => void }> { - return new Promise<{ file: string, cleanupCallback: () => void }>((resolve, reject) => { + private async createTemporaryFile(): Promise<{ file: string; cleanupCallback: () => void }> { + return new Promise<{ file: string; cleanupCallback: () => void }>((resolve, reject) => { tmp.file((error, file, _, cleanupCallback) => { if (error) { reject(new Error(`Can not create temporary file ${file}: ${error}`)); diff --git a/src/pythonRunner.ts b/src/pythonRunner.ts index e55cca2..8f01653 100644 --- a/src/pythonRunner.ts +++ b/src/pythonRunner.ts @@ -14,26 +14,20 @@ class PythonProcessExecution implements IProcessExecution { private readonly pythonProcess: IProcessExecution; - constructor( - args: string[], - configuration: ICommonPythonRunConfiguration - ) { - this.pythonProcess = runProcess( - configuration.pythonPath, - args, - { - cwd: configuration.cwd, - environment: { - ...process.env, - ...configuration.environment, - PYTHONUNBUFFERED: '1', - }, - acceptedExitCodes: configuration.acceptedExitCodes, - }); + constructor(args: string[], configuration: ICommonPythonRunConfiguration) { + this.pythonProcess = runProcess(configuration.pythonPath, args, { + cwd: configuration.cwd, + environment: { + ...process.env, + ...configuration.environment, + PYTHONUNBUFFERED: '1', + }, + acceptedExitCodes: configuration.acceptedExitCodes, + }); this.pid = this.pythonProcess.pid; } - public async complete(): Promise<{ exitCode: number; output: string; }> { + public async complete(): Promise<{ exitCode: number; output: string }> { return this.pythonProcess.complete(); } diff --git a/src/pythonTestAdapter.ts b/src/pythonTestAdapter.ts index 5c73a43..8cf9d0f 100644 --- a/src/pythonTestAdapter.ts +++ b/src/pythonTestAdapter.ts @@ -8,7 +8,7 @@ import { TestRunFinishedEvent, TestRunStartedEvent, TestSuiteEvent, - TestSuiteInfo + TestSuiteInfo, } from 'vscode-test-adapter-api'; import { IConfigurationFactory } from './configuration/configurationFactory'; @@ -39,8 +39,9 @@ interface IPythonTestDebugConfig { } export class PythonTestAdapter implements TestAdapter { - - get tests(): Event { return this.testsEmitter.event; } + get tests(): Event { + return this.testsEmitter.event; + } get testStates(): Event { return this.testStatesEmitter.event; @@ -64,52 +65,52 @@ export class PythonTestAdapter implements TestAdapter { private readonly configurationFactory: IConfigurationFactory, private readonly logger: ILogger ) { - this.disposables = [ - this.testsEmitter, - this.testStatesEmitter, - this.autorunEmitter - ]; + this.disposables = [this.testsEmitter, this.testStatesEmitter, this.autorunEmitter]; this.registerActions(); } private registerActions() { - this.disposables.push(workspace.onDidChangeConfiguration(async configurationChange => { - const sectionsToReload = [ - 'python.pythonPath', - 'python.envFile', - 'python.testing.cwd', - 'python.testing.unittestEnabled', - 'python.testing.unittestArgs', - 'python.testing.pytestEnabled', - 'python.testing.pytestPath', - 'python.testing.pytestArgs', - 'pythonTestExplorer.testFramework' - ]; - - const needsReload = sectionsToReload.some( - section => configurationChange.affectsConfiguration(section, this.workspaceFolder.uri)); - if (needsReload) { - this.logger.log('info', 'Configuration changed, reloading tests'); - this.load(); + this.disposables.push( + workspace.onDidChangeConfiguration(async (configurationChange) => { + const sectionsToReload = [ + 'python.pythonPath', + 'python.envFile', + 'python.testing.cwd', + 'python.testing.unittestEnabled', + 'python.testing.unittestArgs', + 'python.testing.pytestEnabled', + 'python.testing.pytestPath', + 'python.testing.pytestArgs', + 'pythonTestExplorer.testFramework', + ]; - } - })); - - this.disposables.push(workspace.onDidSaveTextDocument(async document => { - const config = await this.configurationFactory.get(this.workspaceFolder); - if (config.autoTestDiscoverOnSaveEnabled()) { - const filename = document.fileName; - if (this.testsByFsPath.has(filename)) { - this.logger.log('debug', 'Test file changed, reloading tests'); - await this.load(); - return; // In case autorun is enabled - execution will be triggered on load. + const needsReload = sectionsToReload.some((section) => + configurationChange.affectsConfiguration(section, this.workspaceFolder.uri) + ); + if (needsReload) { + this.logger.log('info', 'Configuration changed, reloading tests'); + this.load(); } - if (filename.startsWith(this.workspaceFolder.uri.fsPath)) { - this.logger.log('debug', 'Sending autorun event'); - this.autorunEmitter.fire(); + }) + ); + + this.disposables.push( + workspace.onDidSaveTextDocument(async (document) => { + const config = await this.configurationFactory.get(this.workspaceFolder); + if (config.autoTestDiscoverOnSaveEnabled()) { + const filename = document.fileName; + if (this.testsByFsPath.has(filename)) { + this.logger.log('debug', 'Test file changed, reloading tests'); + await this.load(); + return; // In case autorun is enabled - execution will be triggered on load. + } + if (filename.startsWith(this.workspaceFolder.uri.fsPath)) { + this.logger.log('debug', 'Sending autorun event'); + this.autorunEmitter.fire(); + } } - } - })); + }) + ); } public async load(): Promise { @@ -135,10 +136,10 @@ export class PythonTestAdapter implements TestAdapter { try { this.testStatesEmitter.fire({ type: 'started', tests }); const config = await this.configurationFactory.get(this.workspaceFolder); - const testRuns = tests.map(async test => { + const testRuns = tests.map(async (test) => { try { const states = await this.testRunner.run(config, test); - return states.forEach(state => { + return states.forEach((state) => { const testId = state.test as string; if (this.testsById.has(testId) && this.testsById.get(testId)?.type === 'suite') { this.setTestStatesRecursive(testId, state.state, state.message); @@ -165,18 +166,22 @@ export class PythonTestAdapter implements TestAdapter { debugConfiguration.env ); return new Promise(() => { - debug.startDebugging(this.workspaceFolder, { - ...{ - type: 'python', - request: 'launch', - console: 'internalConsole', - }, - ...debugConfiguration, // module, cwd, args, env, - ...launchJsonConfiguration, - }).then( - () => { /* intentionally omitted */ }, - exception => this.logger.log('crit', `Failed to start debugging tests: ${exception}`) - ); + debug + .startDebugging(this.workspaceFolder, { + ...{ + type: 'python', + request: 'launch', + console: 'internalConsole', + }, + ...debugConfiguration, // module, cwd, args, env, + ...launchJsonConfiguration, + }) + .then( + () => { + /* intentionally omitted */ + }, + (exception) => this.logger.log('crit', `Failed to start debugging tests: ${exception}`) + ); }); } @@ -196,10 +201,11 @@ export class PythonTestAdapter implements TestAdapter { return; } test.children.sort((x, y) => x.label.localeCompare(y.label, undefined, { sensitivity: 'base', numeric: true })); - test.children.filter(t => t) - .filter(t => t.type === 'suite') - .map(t => t as TestSuiteInfo) - .forEach(t => this.sortTests(t)); + test.children + .filter((t) => t) + .filter((t) => t.type === 'suite') + .map((t) => t as TestSuiteInfo) + .forEach((t) => this.sortTests(t)); } private saveToMap(test: TestSuiteInfo | TestInfo | undefined) { @@ -211,7 +217,7 @@ export class PythonTestAdapter implements TestAdapter { this.testsByFsPath.set(test.file, test); } if (test.type === 'suite') { - test.children.forEach(child => this.saveToMap(child)); + test.children.forEach((child) => this.saveToMap(child)); } } @@ -226,9 +232,7 @@ export class PythonTestAdapter implements TestAdapter { return; } if (info.type === 'suite') { - info.children.forEach(child => - this.setTestStatesRecursive(child.id, state, message) - ); + info.children.forEach((child) => this.setTestStatesRecursive(child.id, state, message)); } else { this.testStatesEmitter.fire({ type: 'test', @@ -260,21 +264,24 @@ export class PythonTestAdapter implements TestAdapter { } return firstOrDefault( (launchJsonConfiguration.configurations as DebugConfiguration[]) - .filter(cfg => this.isTestConfiguration(cfg)) - .map(cfg => cfg as IPythonTestDebugConfig) - .map(cfg => ({ - env: EnvironmentVariablesLoader.merge(cfg.env || {}, globalEnvironment), + .filter((cfg) => this.isTestConfiguration(cfg)) + .map((cfg) => cfg as IPythonTestDebugConfig) + .map( + (cfg) => + { + env: EnvironmentVariablesLoader.merge(cfg.env || {}, globalEnvironment), - name: `${cfg.name}: ${test}`, - console: cfg.console, - stopOnEntry: cfg.stopOnEntry, - showReturnValue: cfg.showReturnValue, - redirectOutput: cfg.redirectOutput, - debugStdLib: cfg.debugStdLib, - justMyCode: cfg.justMyCode, - subProcess: cfg.subProcess, - envFile: cfg.envFile, - })), + name: `${cfg.name}: ${test}`, + console: cfg.console, + stopOnEntry: cfg.stopOnEntry, + showReturnValue: cfg.showReturnValue, + redirectOutput: cfg.redirectOutput, + debugStdLib: cfg.debugStdLib, + justMyCode: cfg.justMyCode, + subProcess: cfg.subProcess, + envFile: cfg.envFile, + } + ), emptyJsonConfiguration ); } catch (error) { diff --git a/src/testRunner.ts b/src/testRunner.ts index 5bf2b8c..5ddd0e7 100644 --- a/src/testRunner.ts +++ b/src/testRunner.ts @@ -1,7 +1,4 @@ -import { - TestEvent, - TestSuiteInfo -} from 'vscode-test-adapter-api'; +import { TestEvent, TestSuiteInfo } from 'vscode-test-adapter-api'; import { IWorkspaceConfiguration } from './configuration/workspaceConfiguration'; import { IEnvironmentVariables } from './environmentVariablesLoader'; diff --git a/src/testplan/testplanJunitTestStatesParser.ts b/src/testplan/testplanJunitTestStatesParser.ts index 97ffe06..15893c4 100644 --- a/src/testplan/testplanJunitTestStatesParser.ts +++ b/src/testplan/testplanJunitTestStatesParser.ts @@ -1,4 +1,3 @@ - import { EOL } from 'os'; import * as path from 'path'; import { TestEvent } from 'vscode-test-adapter-api'; @@ -47,10 +46,7 @@ interface ITestCaseResult { type TestState = 'passed' | 'failed' | 'skipped'; -export async function parseTestStates( - outputXmlDir: string -): Promise { - +export async function parseTestStates(outputXmlDir: string): Promise { const xmlDirContent = await readDir(outputXmlDir); let testResults: TestEvent[] = []; @@ -80,12 +76,17 @@ function parseTestResults(parserResult: any): TestEvent[] { return []; } const testSuiteResults: ITestSuiteResult[] = parserResult.testsuites.testsuite; - return testSuiteResults.map(testSuiteResult => { - if (!Array.isArray(testSuiteResult.testcase)) { - return []; - } - return testSuiteResult.testcase.map(testcase => mapToTestState(testcase)).filter(x => x).map(x => x!); - }).reduce((r, x) => r.concat(x), []); + return testSuiteResults + .map((testSuiteResult) => { + if (!Array.isArray(testSuiteResult.testcase)) { + return []; + } + return testSuiteResult.testcase + .map((testcase) => mapToTestState(testcase)) + .filter((x) => x) + .map((x) => x!); + }) + .reduce((r, x) => r.concat(x), []); } function mapToTestState(testcase: ITestCaseResult): TestEvent | undefined { @@ -128,9 +129,9 @@ function extractSystemErr(testcase: ITestCaseResult) { return empty(testcase['system-err']) ? '' : testcase['system-err'].join(EOL); } -function extractErrorMessage(errors: { _: string, $: { message: string } }[]): string { +function extractErrorMessage(errors: { _: string; $: { message: string } }[]): string { if (!errors || !errors.length) { return ''; } - return concatNonEmpty(EOL, ...errors.map(e => concatNonEmpty(EOL, e.$.message, e._))); -} \ No newline at end of file + return concatNonEmpty(EOL, ...errors.map((e) => concatNonEmpty(EOL, e.$.message, e._))); +} diff --git a/src/testplan/testplanTestCollectionParser.ts b/src/testplan/testplanTestCollectionParser.ts index dee860d..c5c48ef 100644 --- a/src/testplan/testplanTestCollectionParser.ts +++ b/src/testplan/testplanTestCollectionParser.ts @@ -1,6 +1,6 @@ import { TestInfo, TestSuiteInfo } from 'vscode-test-adapter-api'; -enum TestObjectType{ +enum TestObjectType { APP = 0, SUITE = 1, TEST = 2, @@ -18,14 +18,14 @@ enum TestObjectType{ // Secondary::BetaSuite::passing_testcase_one // Secondary::BetaSuite::passing_testcase_two export function parseTestSuites(content: string): (TestSuiteInfo | TestInfo)[] { - const suites: (TestSuiteInfo | TestInfo)[] = []; const parentStack: TestSuiteInfo[] = []; - content.split(/[\r\n]+/) - .map(line => line.trim()) - .filter(line => line) - .map(line => line!) - .forEach(line => { + content + .split(/[\r\n]+/) + .map((line) => line.trim()) + .filter((line) => line) + .map((line) => line!) + .forEach((line) => { const data = line.split('::'); const testRank = data.length - 1; @@ -59,7 +59,7 @@ export function parseTestSuites(content: string): (TestSuiteInfo | TestInfo)[] { return suites; } -function newTest(data : string[]): TestInfo { +function newTest(data: string[]): TestInfo { return { type: 'test' as 'test', id: data.join(':'), // Testplan can use this format to address a suite/test to run @@ -67,11 +67,11 @@ function newTest(data : string[]): TestInfo { }; } -function newTestSuite(data : string[], testRank : TestObjectType): TestSuiteInfo { +function newTestSuite(data: string[], testRank: TestObjectType): TestSuiteInfo { return { type: 'suite' as 'suite', id: data.join(':'), // Testplan can use this format to address a suite/test to run label: data[testRank], children: [], }; -} \ No newline at end of file +} diff --git a/src/testplan/testplanTestRunner.ts b/src/testplan/testplanTestRunner.ts index 983e0bc..defcb38 100644 --- a/src/testplan/testplanTestRunner.ts +++ b/src/testplan/testplanTestRunner.ts @@ -1,8 +1,6 @@ import * as path from 'path'; import * as tmp from 'tmp'; -import { - TestEvent, TestSuiteInfo -} from 'vscode-test-adapter-api'; +import { TestEvent, TestSuiteInfo } from 'vscode-test-adapter-api'; import { ArgumentParser } from 'argparse'; import { IWorkspaceConfiguration } from '../configuration/workspaceConfiguration'; @@ -21,20 +19,15 @@ import { parseTestSuites } from './testplanTestCollectionParser'; // 2: Test file was not found, however discovery was successful with empty result const TESTPLAN_NON_ERROR_EXIT_CODES = [0, 1, 2]; - interface IRunArguments { junitReportPath?: string; argumentsToPass: string[]; } export class TestplanTestRunner implements ITestRunner { - private readonly testExecutions: Map = new Map(); - constructor( - public readonly adapterId: string, - private readonly logger: ILogger - ) { } + constructor(public readonly adapterId: string, private readonly logger: ILogger) {} public cancel(): void { this.testExecutions.forEach((execution, test) => { @@ -95,9 +88,7 @@ export class TestplanTestRunner implements ITestRunner { const additionalEnvironment = await this.loadEnvironmentVariables(config); const runArguments = this.getRunArguments(test, config.getTestplanConfiguration().testplanArguments); const { dirName, cleanupCallback } = await this.getJunitReportPath(config.getCwd(), runArguments); - const testRunArguments = [ - `--xml=${dirName}` - ].concat(runArguments.argumentsToPass); + const testRunArguments = [`--xml=${dirName}`].concat(runArguments.argumentsToPass); this.logger.log('info', `Running testplan with arguments: ${testRunArguments.join(', ')}`); const testExecution = this.runTestPlan(config, additionalEnvironment, testRunArguments); @@ -113,30 +104,27 @@ export class TestplanTestRunner implements ITestRunner { return states; } - private runTestPlan(config: IWorkspaceConfiguration, env: IEnvironmentVariables, args: string[]) - : IProcessExecution - { + private runTestPlan( + config: IWorkspaceConfiguration, + env: IEnvironmentVariables, + args: string[] + ): IProcessExecution { const testplanPath = config.getTestplanConfiguration().testplanPath(); this.logger.log('info', `Running ${testplanPath} as an executable`); - return runProcess( - config.pythonPath(), - [testplanPath].concat(args), - { - cwd: config.getCwd(), - environment: env, - acceptedExitCodes: TESTPLAN_NON_ERROR_EXIT_CODES, - }); + return runProcess(config.pythonPath(), [testplanPath].concat(args), { + cwd: config.getCwd(), + environment: env, + acceptedExitCodes: TESTPLAN_NON_ERROR_EXIT_CODES, + }); } private async loadEnvironmentVariables(config: IWorkspaceConfiguration): Promise { const envFileEnvironment = await EnvironmentVariablesLoader.load(config.envFile(), process.env, this.logger); - const updatedPythonPath = [ - config.getCwd(), - envFileEnvironment.PYTHONPATH, - process.env.PYTHONPATH - ].filter(item => item).join(path.delimiter); + const updatedPythonPath = [config.getCwd(), envFileEnvironment.PYTHONPATH, process.env.PYTHONPATH] + .filter((item) => item) + .join(path.delimiter); return { ...envFileEnvironment, @@ -148,11 +136,13 @@ export class TestplanTestRunner implements ITestRunner { private async getJunitReportPath( cwd: string, runArguments: IRunArguments - ): Promise<{ dirName: string, cleanupCallback: () => void }> { + ): Promise<{ dirName: string; cleanupCallback: () => void }> { if (runArguments.junitReportPath) { return Promise.resolve({ dirName: path.resolve(cwd, runArguments.junitReportPath), - cleanupCallback: () => { /* intentionally empty */ }, + cleanupCallback: () => { + /* intentionally empty */ + }, }); } return await this.createTemporaryDirectory(); @@ -171,9 +161,7 @@ export class TestplanTestRunner implements ITestRunner { return { junitReportPath: (knownArguments as { xmlpath?: string }).xmlpath, argumentsToPass: argumentsToPass.concat( - test !== this.adapterId ? - ['--patterns', test] : - (knownArguments as { tests?: string[] }).tests || [] + test !== this.adapterId ? ['--patterns', test] : (knownArguments as { tests?: string[] }).tests || [] ), }; } @@ -182,20 +170,14 @@ export class TestplanTestRunner implements ITestRunner { const argumentParser = new ArgumentParser({ exit_on_error: false, }); - argumentParser.add_argument( - '--runpath', - { action: 'store', dest: 'runpath'}); - argumentParser.add_argument( - '--stdout-style', - { action: 'store', dest: 'stdout_style'}); - argumentParser.add_argument( - '--xml', - { action: 'store', dest: 'xmlpath' }); + argumentParser.add_argument('--runpath', { action: 'store', dest: 'runpath' }); + argumentParser.add_argument('--stdout-style', { action: 'store', dest: 'stdout_style' }); + argumentParser.add_argument('--xml', { action: 'store', dest: 'xmlpath' }); return argumentParser; } - private async createTemporaryDirectory(): Promise<{ dirName: string, cleanupCallback: () => void }> { - return new Promise<{ dirName: string, cleanupCallback: () => void }>((resolve, reject) => { + private async createTemporaryDirectory(): Promise<{ dirName: string; cleanupCallback: () => void }> { + return new Promise<{ dirName: string; cleanupCallback: () => void }>((resolve, reject) => { tmp.dir((error, dirName, cleanupCallback) => { if (error) { reject(new Error(`Can not create temporary directory ${dirName}: ${error}`)); diff --git a/src/unittest/unittestScripts.ts b/src/unittest/unittestScripts.ts index 9388836..95dfd47 100644 --- a/src/unittest/unittestScripts.ts +++ b/src/unittest/unittestScripts.ts @@ -1,5 +1,3 @@ - - export const TEST_RESULT_PREFIX = 'TEST_EXECUTION_RESULT'; export const UNITTEST_TEST_RUNNER_SCRIPT = ` diff --git a/src/unittest/unittestSuitParser.ts b/src/unittest/unittestSuitParser.ts index 0396a35..7e0ca6b 100644 --- a/src/unittest/unittestSuitParser.ts +++ b/src/unittest/unittestSuitParser.ts @@ -11,7 +11,7 @@ const DISCOVERED_TESTS_END_MARK = '==DISCOVERED TESTS END=='; interface IDiscoveryResultJson { tests: { id: string }[]; - errors: { class: string, message: number }[]; + errors: { class: string; message: number }[]; } export function parseTestSuites(content: string, cwd: string): (TestSuiteInfo | TestInfo)[] { @@ -23,49 +23,51 @@ export function parseTestSuites(content: string, cwd: string): (TestSuiteInfo | return []; } const allTests = (discoveryResult.tests || []) - .map(line => line.id.trim()) - .filter(id => id) - .map(id => splitTestId(id)) - .filter(id => id) - .map(id => id!); + .map((line) => line.id.trim()) + .filter((id) => id) + .map((id) => splitTestId(id)) + .filter((id) => id) + .map((id) => id!); - const aggregatedErrors = Array.from(groupBy((discoveryResult.errors || []), e => e.class).entries()) + const aggregatedErrors = Array.from(groupBy(discoveryResult.errors || [], (e) => e.class).entries()) .map(([className, messages]) => ({ id: splitTestId(className), - message: messages.map(e => e.message).join(os.EOL), + message: messages.map((e) => e.message).join(os.EOL), })) - .filter(e => e.id) - .map(e => ({ + .filter((e) => e.id) + .map((e) => ({ id: e.id!, file: errorSuiteFilePathBySuiteId(cwd, e.id!.testId), message: e.message, })); - const discoveryErrorSuites = aggregatedErrors.map(({ id, file, message }) => ({ - type: 'test' as 'test', - id: id.testId, - file, - label: id.testLabel, - errored: true, - message, - })); - const suites = Array.from(groupBy(allTests, t => t.suiteId).entries()) - .map(([suiteId, tests]) => { - const suiteFile = filePathBySuiteId(cwd, suiteId); - return { - type: 'suite' as 'suite', - id: suiteId, - label: suiteId.substring(suiteId.lastIndexOf('.') + 1), + const discoveryErrorSuites = aggregatedErrors.map( + ({ id, file, message }) => + { + type: 'test' as 'test', + id: id.testId, + file, + label: id.testLabel, + errored: true, + message, + } + ); + const suites = Array.from(groupBy(allTests, (t) => t.suiteId).entries()).map(([suiteId, tests]) => { + const suiteFile = filePathBySuiteId(cwd, suiteId); + return { + type: 'suite' as 'suite', + id: suiteId, + label: suiteId.substring(suiteId.lastIndexOf('.') + 1), + file: suiteFile, + tooltip: suiteId, + children: tests.map((test) => ({ + type: 'test' as 'test', + id: test.testId, + label: test.testLabel, file: suiteFile, - tooltip: suiteId, - children: tests.map(test => ({ - type: 'test' as 'test', - id: test.testId, - label: test.testLabel, - file: suiteFile, - tooltip: test.testId, - })), - }; - }); + tooltip: test.testId, + })), + }; + }); return suites.concat(discoveryErrorSuites); } @@ -73,14 +75,14 @@ export function parseTestSuites(content: string, cwd: string): (TestSuiteInfo | export function parseTestStates(output: string): TestEvent[] { const testEvents = output .split(/\r?\n/g) - .map(line => line.trim()) - .map(line => tryParseTestState(line)) - .filter(line => line) - .map(line => line!); + .map((line) => line.trim()) + .map((line) => tryParseTestState(line)) + .filter((line) => line) + .map((line) => line!); // HACK: Remove duplicates by id so it does not appear in the debug console more than once, // because right now script is printing test results multiple times. - return distinctBy(testEvents, e => e.test); + return distinctBy(testEvents, (e) => e.test); } function tryParseTestState(line: string): TestEvent | undefined { diff --git a/src/unittest/unittestTestRunner.ts b/src/unittest/unittestTestRunner.ts index 4186959..723d8a6 100644 --- a/src/unittest/unittestTestRunner.ts +++ b/src/unittest/unittestTestRunner.ts @@ -1,7 +1,5 @@ import * as path from 'path'; -import { - TestEvent, TestSuiteInfo -} from 'vscode-test-adapter-api'; +import { TestEvent, TestSuiteInfo } from 'vscode-test-adapter-api'; import { IWorkspaceConfiguration } from '../configuration/workspaceConfiguration'; import { EnvironmentVariablesLoader } from '../environmentVariablesLoader'; @@ -17,10 +15,7 @@ import { parseTestStates, parseTestSuites } from './unittestSuitParser'; export class UnittestTestRunner implements ITestRunner { private readonly testExecutions: Map = new Map(); - constructor( - public readonly adapterId: string, - private readonly logger: ILogger - ) { } + constructor(public readonly adapterId: string, private readonly logger: ILogger) {} public cancel(): void { this.testExecutions.forEach((execution, test) => { @@ -37,7 +32,10 @@ export class UnittestTestRunner implements ITestRunner { const additionalEnvironment = await EnvironmentVariablesLoader.load(config.envFile(), process.env, this.logger); const unittestArguments = config.getUnittestConfiguration().unittestArguments; const testId = this.normalizeDebugTestId(unittestArguments.startDirectory, config.getCwd(), test); - this.logger.log('info', `Debugging test "${testId}" using python path "${config.pythonPath()}" in ${config.getCwd()}`); + this.logger.log( + 'info', + `Debugging test "${testId}" using python path "${config.pythonPath()}" in ${config.getCwd()}` + ); return { module: 'unittest', cwd: config.getCwd(), @@ -54,8 +52,11 @@ export class UnittestTestRunner implements ITestRunner { const additionalEnvironment = await EnvironmentVariablesLoader.load(config.envFile(), process.env, this.logger); const unittestArguments = config.getUnittestConfiguration().unittestArguments; - this.logger.log('info', `Discovering tests using python path "${config.pythonPath()}" in ${config.getCwd()} ` + - `with pattern ${unittestArguments.pattern} and start directory ${unittestArguments.startDirectory}`); + this.logger.log( + 'info', + `Discovering tests using python path "${config.pythonPath()}" in ${config.getCwd()} ` + + `with pattern ${unittestArguments.pattern} and start directory ${unittestArguments.startDirectory}` + ); const result = await runScript({ pythonPath: config.pythonPath(), @@ -65,10 +66,7 @@ export class UnittestTestRunner implements ITestRunner { environment: additionalEnvironment, }).complete(); - const tests = parseTestSuites( - result.output, - path.resolve(config.getCwd(), unittestArguments.startDirectory) - ); + const tests = parseTestSuites(result.output, path.resolve(config.getCwd(), unittestArguments.startDirectory)); if (empty(tests)) { this.logger.log('warn', 'No tests discovered'); return undefined; @@ -90,16 +88,20 @@ export class UnittestTestRunner implements ITestRunner { } const unittestArguments = config.getUnittestConfiguration().unittestArguments; - this.logger.log('info', `Running tests using python path "${config.pythonPath()}" in ${config.getCwd()} ` + - `with pattern ${unittestArguments.pattern} and start directory ${unittestArguments.startDirectory}`); + this.logger.log( + 'info', + `Running tests using python path "${config.pythonPath()}" in ${config.getCwd()} ` + + `with pattern ${unittestArguments.pattern} and start directory ${unittestArguments.startDirectory}` + ); const additionalEnvironment = await EnvironmentVariablesLoader.load(config.envFile(), process.env, this.logger); const testExecution = runScript({ pythonPath: config.pythonPath(), script: UNITTEST_TEST_RUNNER_SCRIPT, cwd: config.getCwd(), - args: test !== this.adapterId ? - ['run', unittestArguments.startDirectory, unittestArguments.pattern, test] : - ['run', unittestArguments.startDirectory, unittestArguments.pattern], + args: + test !== this.adapterId + ? ['run', unittestArguments.startDirectory, unittestArguments.pattern, test] + : ['run', unittestArguments.startDirectory, unittestArguments.pattern], environment: additionalEnvironment, }); this.testExecutions.set(test, testExecution); @@ -109,9 +111,9 @@ export class UnittestTestRunner implements ITestRunner { } private normalizeDebugTestId(startDirectory: string, cwd: string, relativeTestId: string): string { - const relativeStartDirectory = path.isAbsolute(startDirectory) ? - path.relative(cwd, startDirectory) : - startDirectory; + const relativeStartDirectory = path.isAbsolute(startDirectory) + ? path.relative(cwd, startDirectory) + : startDirectory; if (relativeStartDirectory === '.') { return relativeTestId; } diff --git a/src/utilities/collections.ts b/src/utilities/collections.ts index 503fa2e..9e1f495 100644 --- a/src/utilities/collections.ts +++ b/src/utilities/collections.ts @@ -1,4 +1,3 @@ - export function empty(x: T[]) { return !x || !x.length; } @@ -33,7 +32,7 @@ export function groupBy(values: T[], key: (v: T) => U) { export function distinctBy(values: T[], key: (v: T) => U): T[] { const byKey = new Map(); - values.forEach(x => { + values.forEach((x) => { byKey.set(key(x), x); }); return Array.from(byKey.values()); diff --git a/src/utilities/fs.ts b/src/utilities/fs.ts index 148fb60..a88484a 100644 --- a/src/utilities/fs.ts +++ b/src/utilities/fs.ts @@ -1,11 +1,10 @@ - import * as fs from 'fs'; import util = require('util'); export const readDir = util.promisify(fs.readdir); export function isFileExists(file: fs.PathLike): Promise { return new Promise((resolve, _) => { - fs.exists(file, exist => { + fs.exists(file, (exist) => { resolve(exist); }); }); diff --git a/src/utilities/strings.ts b/src/utilities/strings.ts index 7626135..58b5dc8 100644 --- a/src/utilities/strings.ts +++ b/src/utilities/strings.ts @@ -1,4 +1,3 @@ - /** * HACK: for #232. * For some reason String.startsWith method is being replaced in @@ -23,5 +22,5 @@ export function startsWith(s: string, p: string, offset: number = 0): boolean { * @returns All non-empty strings of an array contatenated into a string, separated by the specified glue string. */ export function concatNonEmpty(glue: string, ...s: string[]): string { - return s.filter(p => p).join(glue); -} \ No newline at end of file + return s.filter((p) => p).join(glue); +} diff --git a/src/utilities/tests.ts b/src/utilities/tests.ts index b97c48c..7bad040 100644 --- a/src/utilities/tests.ts +++ b/src/utilities/tests.ts @@ -1,37 +1,35 @@ - import { groupBy, empty } from './collections'; export function getTestOutputBySplittingString(output: string, stringToSplitWith: string): string { const split = output.split(stringToSplitWith); - return split && split.pop() || ''; + return (split && split.pop()) || ''; } export function setDescriptionForEqualLabels( - values: { id: string, label: string, description?: string }[], + values: { id: string; label: string; description?: string }[], idSeparator: string ) { const updatedLabels = mapUniqueLabelsById( - values.filter(v => v.id.endsWith(v.label)) // Assuming label is last part of id - .map(v => ({ ...v, prefix: '' })), + values + .filter((v) => v.id.endsWith(v.label)) // Assuming label is last part of id + .map((v) => ({ ...v, prefix: '' })), idSeparator ); - values.filter(v => updatedLabels.has(v.id)) - .filter(v => updatedLabels.get(v.id)!.prefix) - .forEach(v => { + values + .filter((v) => updatedLabels.has(v.id)) + .filter((v) => updatedLabels.get(v.id)!.prefix) + .forEach((v) => { v.description = `${updatedLabels.get(v.id)!.prefix}`; }); } -function mapUniqueLabelsById( - values: { id: string, prefix: string | undefined, label: string }[], - idSeparator: string -) { - const uniqueLabelsById = new Map(); - const labelGroups = groupBy(values, v => prependPrefix(v.prefix, idSeparator, v.label)); +function mapUniqueLabelsById(values: { id: string; prefix: string | undefined; label: string }[], idSeparator: string) { + const uniqueLabelsById = new Map(); + const labelGroups = groupBy(values, (v) => prependPrefix(v.prefix, idSeparator, v.label)); Array.from(labelGroups.entries()) .filter(([_, group]) => group.length > 1) .map(([label, group]) => { - const extendedPrefixGroup = group.map(v => { + const extendedPrefixGroup = group.map((v) => { const idPrefix = v.id.substring(0, v.id.length - label.length - idSeparator.length); const labelPrefix = extractLastElement(idPrefix.split(idSeparator)); return { @@ -40,7 +38,7 @@ function mapUniqueLabelsById( label: v.label, }; }); - extendedPrefixGroup.forEach(v => uniqueLabelsById.set(v.id, v)); + extendedPrefixGroup.forEach((v) => uniqueLabelsById.set(v.id, v)); mapUniqueLabelsById(extendedPrefixGroup, idSeparator).forEach((v, k) => uniqueLabelsById.set(k, v)); }); return uniqueLabelsById; diff --git a/test/mocha-runner.ts b/test/mocha-runner.ts index 8956fbc..d0b26cf 100644 --- a/test/mocha-runner.ts +++ b/test/mocha-runner.ts @@ -10,7 +10,7 @@ export function run(): Promise { // Create the mocha test const mochaRunner = new Mocha({ ...{ - ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) + ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) color: true, // colored output from test results slow: 1000, timeout: 10000, @@ -27,11 +27,11 @@ export function run(): Promise { } // Add files to the test suite - files.forEach(f => mochaRunner.addFile(path.resolve(testsRoot, f))); + files.forEach((f) => mochaRunner.addFile(path.resolve(testsRoot, f))); try { // Run the mocha test - mochaRunner.run(failures => { + mochaRunner.run((failures) => { if (failures > 0) { reject(new Error(`${failures} tests failed.`)); } else { diff --git a/test/test_samples/pytest/.vscode/launch.json b/test/test_samples/pytest/.vscode/launch.json index e1214fe..f20b985 100644 --- a/test/test_samples/pytest/.vscode/launch.json +++ b/test/test_samples/pytest/.vscode/launch.json @@ -14,4 +14,4 @@ "purpose": ["debug-test"] } ] -} \ No newline at end of file +} diff --git a/test/test_samples/pytest/.vscode/settings.json b/test/test_samples/pytest/.vscode/settings.json index d9bfea7..8c6cb01 100644 --- a/test/test_samples/pytest/.vscode/settings.json +++ b/test/test_samples/pytest/.vscode/settings.json @@ -1,5 +1,5 @@ { - "python.envFile": "../.env", - "python.testing.pytestEnabled": true, - "python.testing.pytestArgs": ["--rootdir", "test/inner_tests", "--doctest-modules"] + "python.envFile": "../.env", + "python.testing.pytestEnabled": true, + "python.testing.pytestArgs": ["--rootdir", "test/inner_tests", "--doctest-modules"] } diff --git a/test/test_samples/pytest/test/test_minimal.tavern.yaml b/test/test_samples/pytest/test/test_minimal.tavern.yaml index 6f3cb8c..0741e23 100644 --- a/test/test_samples/pytest/test/test_minimal.tavern.yaml +++ b/test/test_samples/pytest/test/test_minimal.tavern.yaml @@ -1,21 +1,21 @@ --- test_name: Get data from JSON placeholder API_passed stages: - - name: Make sure we have the right ID - request: - url: https://jsonplaceholder.typicode.com/posts/1 - method: GET - response: - status_code: 200 - body: - id: 1 + - name: Make sure we have the right ID + request: + url: https://jsonplaceholder.typicode.com/posts/1 + method: GET + response: + status_code: 200 + body: + id: 1 --- test_name: Delete data from JSON placeholder API_passed stages: - - name: Make sure we have the right ID - request: - url: https://jsonplaceholder.typicode.com/posts/2 - method: DELETE - response: - status_code: 200 \ No newline at end of file + - name: Make sure we have the right ID + request: + url: https://jsonplaceholder.typicode.com/posts/2 + method: DELETE + response: + status_code: 200 diff --git a/test/test_samples/pytest_test_cancellation/.vscode/settings.json b/test/test_samples/pytest_test_cancellation/.vscode/settings.json index d50b09d..57c09cb 100644 --- a/test/test_samples/pytest_test_cancellation/.vscode/settings.json +++ b/test/test_samples/pytest_test_cancellation/.vscode/settings.json @@ -1,4 +1,4 @@ { - "python.pythonPath": "python", - "python.testing.pytestEnabled": true + "python.pythonPath": "python", + "python.testing.pytestEnabled": true } diff --git a/test/test_samples/testplan/.vscode/settings.json b/test/test_samples/testplan/.vscode/settings.json index b623af1..7b28f54 100644 --- a/test/test_samples/testplan/.vscode/settings.json +++ b/test/test_samples/testplan/.vscode/settings.json @@ -1,3 +1,3 @@ { - "pythonTestExplorer.testplanEnabled": true + "pythonTestExplorer.testplanEnabled": true } diff --git a/test/test_samples/testplan_test_cancellation/.vscode/settings.json b/test/test_samples/testplan_test_cancellation/.vscode/settings.json index 7d5ff56..4b5ac1f 100644 --- a/test/test_samples/testplan_test_cancellation/.vscode/settings.json +++ b/test/test_samples/testplan_test_cancellation/.vscode/settings.json @@ -1,4 +1,4 @@ { - "python.pythonPath": "python", - "pythonTestExplorer.testplanEnabled": true + "python.pythonPath": "python", + "pythonTestExplorer.testplanEnabled": true } diff --git a/test/test_samples/workspaces/bad_env_file/.vscode/settings.json b/test/test_samples/workspaces/bad_env_file/.vscode/settings.json index fd03a1c..b313d9b 100644 --- a/test/test_samples/workspaces/bad_env_file/.vscode/settings.json +++ b/test/test_samples/workspaces/bad_env_file/.vscode/settings.json @@ -1,4 +1,4 @@ { - "python.testing.pyTestEnabled": true, - "python.testing.unittestEnabled": true + "python.testing.pyTestEnabled": true, + "python.testing.unittestEnabled": true } diff --git a/test/test_samples/workspaces/empty_configuration/.vscode/settings.json b/test/test_samples/workspaces/empty_configuration/.vscode/settings.json index 2c63c08..0967ef4 100644 --- a/test/test_samples/workspaces/empty_configuration/.vscode/settings.json +++ b/test/test_samples/workspaces/empty_configuration/.vscode/settings.json @@ -1,2 +1 @@ -{ -} +{} diff --git a/test/test_samples/workspaces/not_existent_env_file/.vscode/settings.json b/test/test_samples/workspaces/not_existent_env_file/.vscode/settings.json index 5ba9628..f3597f9 100644 --- a/test/test_samples/workspaces/not_existent_env_file/.vscode/settings.json +++ b/test/test_samples/workspaces/not_existent_env_file/.vscode/settings.json @@ -1,5 +1,5 @@ { - "python.testing.pyTestEnabled": true, - "python.testing.unittestEnabled": true, - "python.envFile": "/some/not/existent/path/.env" + "python.testing.pyTestEnabled": true, + "python.testing.unittestEnabled": true, + "python.envFile": "/some/not/existent/path/.env" } diff --git a/test/test_samples/workspaces/python_extension_configured_testplan/.vscode/settings.json b/test/test_samples/workspaces/python_extension_configured_testplan/.vscode/settings.json index 53ff2c9..ba1961e 100644 --- a/test/test_samples/workspaces/python_extension_configured_testplan/.vscode/settings.json +++ b/test/test_samples/workspaces/python_extension_configured_testplan/.vscode/settings.json @@ -1,4 +1,4 @@ { "python.pythonPath": "python", "python.testing.cwd": "/some/unittest/cwd" -} \ No newline at end of file +} diff --git a/test/test_samples/workspaces/python_extension_configured_unittest/.vscode/settings.json b/test/test_samples/workspaces/python_extension_configured_unittest/.vscode/settings.json index 26662b4..5417488 100644 --- a/test/test_samples/workspaces/python_extension_configured_unittest/.vscode/settings.json +++ b/test/test_samples/workspaces/python_extension_configured_unittest/.vscode/settings.json @@ -2,4 +2,4 @@ "python.testing.unittestEnabled": true, "python.pythonPath": "python", "python.testing.cwd": "/some/unittest/cwd" -} \ No newline at end of file +} diff --git a/test/tests/environmentParsing.test.ts b/test/tests/environmentParsing.test.ts index 14559f4..1cfe62f 100644 --- a/test/tests/environmentParsing.test.ts +++ b/test/tests/environmentParsing.test.ts @@ -2,7 +2,11 @@ import { expect } from 'chai'; import 'mocha'; import * as path from 'path'; -import { IPytestConfiguration, ITestplanConfiguration, IUnittestConfiguration } from '../../src/configuration/workspaceConfiguration'; +import { + IPytestConfiguration, + ITestplanConfiguration, + IUnittestConfiguration, +} from '../../src/configuration/workspaceConfiguration'; import { PytestTestRunner } from '../../src/pytest/pytestTestRunner'; import { TestplanTestRunner } from '../../src/testplan/testplanTestRunner'; import { UnittestTestRunner } from '../../src/unittest/unittestTestRunner'; @@ -21,7 +25,7 @@ import { getPythonExecutable } from '../utils/testConfiguration'; { name: 'testplan', runner: new TestplanTestRunner('some-id', logger()), - } + }, ].forEach(({ name, runner }) => { suite(`Environment variable parsing with ${name} runner`, () => { test('should not fail on bad .env file', async () => { diff --git a/test/tests/idGenerator.test.ts b/test/tests/idGenerator.test.ts index d5fc781..91ee371 100644 --- a/test/tests/idGenerator.test.ts +++ b/test/tests/idGenerator.test.ts @@ -4,7 +4,7 @@ import 'mocha'; import { nextId } from '../../src/idGenerator'; function hasDuplicates(values: T[]) { - return (new Set(values)).size !== values.length; + return new Set(values).size !== values.length; } suite('Id generator', () => { diff --git a/test/tests/placeholderAwareWorkspaceConfiguration.test.ts b/test/tests/placeholderAwareWorkspaceConfiguration.test.ts index 1621126..86a67e5 100644 --- a/test/tests/placeholderAwareWorkspaceConfiguration.test.ts +++ b/test/tests/placeholderAwareWorkspaceConfiguration.test.ts @@ -8,7 +8,7 @@ import { IPytestConfiguration, ITestplanConfiguration, IUnittestConfiguration, - IWorkspaceConfiguration + IWorkspaceConfiguration, } from '../../src/configuration/workspaceConfiguration'; import { findWorkspaceFolder, logger } from '../utils/helpers'; @@ -67,9 +67,7 @@ suite('Placeholder aware workspace configuration', () => { const wfPath = getWorkspaceFolder().uri.fsPath; expect(configuration.pythonPath()).to.be.eq(path.resolve(wfPath, 'some', 'local', 'python')); expect(configuration.getCwd()).to.be.eq(path.resolve('/some', 'prefix', 'some', 'cwd', 'suffix')); - expect( - configuration.getUnittestConfiguration().unittestArguments.startDirectory - ).to.be.eq(wfPath); + expect(configuration.getUnittestConfiguration().unittestArguments.startDirectory).to.be.eq(wfPath); }); test('should resolve values from configuration without placeholders', () => { @@ -103,9 +101,7 @@ suite('Placeholder aware workspace configuration', () => { return { pytestPath: () => 'pytest', isPytestEnabled: true, - pytestArguments: [ - '--result-log=${workspaceFolder}/${env:RELATIVE_PYTEST_LOG_PATH}' - ], + pytestArguments: ['--result-log=${workspaceFolder}/${env:RELATIVE_PYTEST_LOG_PATH}'], }; }, getTestplanConfiguration(): ITestplanConfiguration { @@ -120,12 +116,10 @@ suite('Placeholder aware workspace configuration', () => { const wfPath = getWorkspaceFolder().uri.fsPath; expect(configuration.pythonPath()).to.be.eq('python'); expect(configuration.getCwd()).to.be.eq(path.resolve(wfPath, '..', 'some', 'prefix', 'some', 'cwd', 'suffix')); - expect( - configuration.getUnittestConfiguration().unittestArguments.startDirectory - ).to.be.eq(wfPath); - expect( - configuration.getPytestConfiguration().pytestArguments - ).to.have.members([`--result-log=${wfPath + '/some/path/to/log'}`]); + expect(configuration.getUnittestConfiguration().unittestArguments.startDirectory).to.be.eq(wfPath); + expect(configuration.getPytestConfiguration().pytestArguments).to.have.members([ + `--result-log=${wfPath + '/some/path/to/log'}`, + ]); }); test('should resolve relative path placeholders from configuration', () => { @@ -217,9 +211,9 @@ suite('Placeholder aware workspace configuration', () => { const wfPath = getWorkspaceFolder().uri.fsPath; expect(configuration.pythonPath()).to.be.eq('python'); expect(configuration.getCwd()).to.be.eq(path.normalize(path.resolve(wfPath, 'some_cwd'))); - expect( - configuration.getUnittestConfiguration().unittestArguments.startDirectory - ).to.be.eq(path.normalize(path.resolve(wfPath, 'test'))); + expect(configuration.getUnittestConfiguration().unittestArguments.startDirectory).to.be.eq( + path.normalize(path.resolve(wfPath, 'test')) + ); }); test('should resolve home path from configuration', () => { @@ -272,7 +266,7 @@ suite('Placeholder aware workspace configuration', () => { [ ['${workspaceFolder}', getWorkspaceFolder().uri.fsPath], ['${workspaceRoot}', getWorkspaceFolder().uri.fsPath], - ['${cwd}', getWorkspaceFolder().uri.fsPath] + ['${cwd}', getWorkspaceFolder().uri.fsPath], ].forEach(([placeholder, expectedPath]) => { test(`should resolve placeholder ${placeholder} from configuration`, () => { const configuration = getConfiguration({ @@ -319,7 +313,7 @@ suite('Placeholder aware workspace configuration', () => { [ ['${workspaceFolderBasename}', getWorkspaceFolder().name], - ['${workspaceRootFolderName}', getWorkspaceFolder().name] + ['${workspaceRootFolderName}', getWorkspaceFolder().name], ].forEach(([placeholder, expectedPath]) => { test(`should resolve placeholder ${placeholder} from configuration`, () => { const configuration = getConfiguration({ @@ -361,7 +355,7 @@ suite('Placeholder aware workspace configuration', () => { }); expect(configuration.pythonPath()).to.be.eq( - path.normalize(path.resolve('/some', 'prefix' , expectedPath, 'some', 'local', 'python')) + path.normalize(path.resolve('/some', 'prefix', expectedPath, 'some', 'local', 'python')) ); }); }); diff --git a/test/tests/pytestArguments.test.ts b/test/tests/pytestArguments.test.ts index 3ce106f..463da80 100644 --- a/test/tests/pytestArguments.test.ts +++ b/test/tests/pytestArguments.test.ts @@ -2,51 +2,51 @@ import { expect } from 'chai'; import 'mocha'; import * as path from 'path'; import * as fs from 'fs'; -import { - TestSuiteInfo -} from 'vscode-test-adapter-api'; +import { TestSuiteInfo } from 'vscode-test-adapter-api'; import { isFileExists } from '../../src/utilities/fs'; import { IWorkspaceConfiguration } from '../../src/configuration/workspaceConfiguration'; import { PytestTestRunner } from '../../src/pytest/pytestTestRunner'; -import { createPytestConfiguration, extractExpectedState, extractErroredTests, findTestSuiteByLabel, logger } from '../utils/helpers'; +import { + createPytestConfiguration, + extractExpectedState, + extractErroredTests, + findTestSuiteByLabel, + logger, +} from '../utils/helpers'; import { PYTEST_EXPECTED_SUITES_LIST_WITHOUT_ERRORS } from '../utils/pytest'; suite('Pytest test discovery with additional arguments', async () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest', - [ - '--rootdir=test/inner_tests', - '--trace', - '--cache-show', - '--doctest-modules', - '--collect-only', - '--junitxml=sample.xml', - '--ignore=test/import_error_tests' - ]); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest', [ + '--rootdir=test/inner_tests', + '--trace', + '--cache-show', + '--doctest-modules', + '--collect-only', + '--junitxml=sample.xml', + '--ignore=test/import_error_tests', + ]); const runner = new PytestTestRunner('some-id', logger()); test('should discover tests', async () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.be.empty; - const labels = mainSuite!.children.map(x => x.label); + const labels = mainSuite!.children.map((x) => x.label); expect(labels).to.have.members(PYTEST_EXPECTED_SUITES_LIST_WITHOUT_ERRORS); }); }); suite('Run pytest tests with additional arguments', () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest', - [ - '--rootdir', - 'test/inner_tests', - '--trace', - '--doctest-modules', - '--collect-only', - '--junitxml=sample.xml', - '--exitfirst', - '--ignore=test/import_error_tests' - ]); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest', [ + '--rootdir', + 'test/inner_tests', + '--trace', + '--doctest-modules', + '--collect-only', + '--junitxml=sample.xml', + '--exitfirst', + '--ignore=test/import_error_tests', + ]); const runner = new PytestTestRunner('some-id', logger()); test('should run all tests', async () => { @@ -56,7 +56,7 @@ suite('Run pytest tests with additional arguments', () => { expect(mainSuite!.label).to.be.eq('Pytest tests'); const states = await runner.run(config, runner.adapterId); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -67,9 +67,9 @@ suite('Run pytest tests with additional arguments', () => { suite: 'arithmetic.py', cases: [ { file: 'src/arithmetic.py', case: '::arithmetic.add_failed' }, - { file: 'src/arithmetic.py', case: '::arithmetic.mul_passed' } + { file: 'src/arithmetic.py', case: '::arithmetic.mul_passed' }, ], - } + }, ].forEach(({ suite, cases }) => { test(`should run doctest ${suite} suite`, async () => { const mainSuite = await runner.load(config); @@ -80,20 +80,17 @@ suite('Run pytest tests with additional arguments', () => { const states = await runner.run(config, suiteToRun!.id); expect(states).to.be.not.empty; const cwd = config.getCwd(); - expect(states.map(s => s.test)).to.have.deep.members( - cases.map(c => path.resolve(cwd, c.file) + c.case) + expect(states.map((s) => s.test)).to.have.deep.members( + cases.map((c) => path.resolve(cwd, c.file) + c.case) ); - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); }); }); - [ - 'arithmetic.mul_passed', - 'arithmetic.add_failed' - ].forEach(testMethod => { + ['arithmetic.mul_passed', 'arithmetic.add_failed'].forEach((testMethod) => { test(`should run doctest ${testMethod} test`, async () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; @@ -102,7 +99,7 @@ suite('Run pytest tests with additional arguments', () => { expect(suite).to.be.not.undefined; const states = await runner.run(config, suite!.id); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -114,64 +111,61 @@ suite('Filter pytest tests by mark arguments', () => { const runner = new PytestTestRunner('some-id', logger()); const markedTests = [ { module: path.join('test', 'inner_tests', 'add_test.py'), case: '::test_one_plus_two_is_three_passed' }, - { module: path.join('test', 'other_tests', 'add_test.py'), case: '::test_same_filename_one_plus_two_is_three_passed' } + { + module: path.join('test', 'other_tests', 'add_test.py'), + case: '::test_same_filename_one_plus_two_is_three_passed', + }, ]; test('should discover only tests with specific mark', async () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest', - [ - '--ignore=test/import_error_tests', - '-m', - 'add_test_passed' - ]); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest', [ + '--ignore=test/import_error_tests', + '-m', + 'add_test_passed', + ]); const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.be.empty; expect(mainSuite!.label).to.be.eq('Pytest tests'); - const labels = mainSuite!.children.map(x => x.file); - expect(labels).to.have.members(markedTests.map(t => path.join(config.getCwd(), t.module))); + const labels = mainSuite!.children.map((x) => x.file); + expect(labels).to.have.members(markedTests.map((t) => path.join(config.getCwd(), t.module))); }); test('should run only tests with specific mark', async () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest', - [ - '--ignore=test/import_error_tests', - '-m', - 'add_test_passed' - ]); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest', [ + '--ignore=test/import_error_tests', + '-m', + 'add_test_passed', + ]); const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.be.empty; expect(mainSuite!.label).to.be.eq('Pytest tests'); const states = await runner.run(config, runner.adapterId); expect(states).to.be.not.empty; - const labels = states.map(x => x.test); - expect(labels).to.have.members(markedTests.map(t => path.join(config.getCwd(), t.module) + t.case)); - states.forEach(state => { + const labels = states.map((x) => x.test); + expect(labels).to.have.members(markedTests.map((t) => path.join(config.getCwd(), t.module) + t.case)); + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); }); test('should not run tests with specific mark', async () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest', - [ - '--ignore=test/import_error_tests', - '-m', - 'not add_test_passed' - ]); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest', [ + '--ignore=test/import_error_tests', + '-m', + 'not add_test_passed', + ]); const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.be.empty; expect(mainSuite!.label).to.be.eq('Pytest tests'); const states = await runner.run(config, runner.adapterId); expect(states).to.be.not.empty; - const labels = states.map(x => x.test); - expect(labels).not.to.have.members(markedTests.map(t => path.join(config.getCwd(), t.module) + t.case)); - states.forEach(state => { + const labels = states.map((x) => x.test); + expect(labels).not.to.have.members(markedTests.map((t) => path.join(config.getCwd(), t.module) + t.case)); + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -179,25 +173,20 @@ suite('Filter pytest tests by mark arguments', () => { }); suite('Pytest tests with additional positional arguments', () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest', - [ - '--rootdir', - 'test/inner_tests', - 'test/inner_tests', - 'test/other_tests' - ]); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest', [ + '--rootdir', + 'test/inner_tests', + 'test/inner_tests', + 'test/other_tests', + ]); const runner = new PytestTestRunner('some-id', logger()); test('should discover tests', async () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.be.empty; - const expectedSuites = [ - 'add_test.py', - 'add_test.py' - ]; - const labels = mainSuite!.children.map(x => x.label); + const expectedSuites = ['add_test.py', 'add_test.py']; + const labels = mainSuite!.children.map((x) => x.label); expect(labels).to.have.members(expectedSuites); }); @@ -208,14 +197,16 @@ suite('Pytest tests with additional positional arguments', () => { expect(mainSuite!.label).to.be.eq('Pytest tests'); const states = await runner.run(config, runner.adapterId); expect(states).to.be.not.empty; - const labels = states.map(x => x.test); + const labels = states.map((x) => x.test); expect(labels).to.have.members([ path.join(config.getCwd(), 'test', 'inner_tests', 'add_test.py') + '::test_one_plus_two_is_three_passed', path.join(config.getCwd(), 'test', 'inner_tests', 'add_test.py') + '::test_two_plus_two_is_five_failed', - path.join(config.getCwd(), 'test', 'other_tests', 'add_test.py') + '::test_same_filename_one_plus_two_is_three_passed', - path.join(config.getCwd(), 'test', 'other_tests', 'add_test.py') + '::test_same_filename_two_plus_two_is_five_failed' + path.join(config.getCwd(), 'test', 'other_tests', 'add_test.py') + + '::test_same_filename_one_plus_two_is_three_passed', + path.join(config.getCwd(), 'test', 'other_tests', 'add_test.py') + + '::test_same_filename_two_plus_two_is_five_failed', ]); - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -227,19 +218,17 @@ suite('Use junit-xml argument for pytest tests', () => { test('should create junit-xml report by custom path', async () => { const now = new Date().getTime(); - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest', - [ - '--ignore=test/import_error_tests', - `--junitxml=example_${now}.xml` - ]); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest', [ + '--ignore=test/import_error_tests', + `--junitxml=example_${now}.xml`, + ]); const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.be.empty; const states = await runner.run(config, runner.adapterId); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -281,11 +270,9 @@ suite('Run pytest suite with pytest.ini in subdirectory', () => { const runner = new PytestTestRunner('some-id', logger()); test('should discover and run all tests', async () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest', - [ - '--ignore=test/import_error_tests' - ]); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest', [ + '--ignore=test/import_error_tests', + ]); const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.be.empty; @@ -299,21 +286,19 @@ suite('Run pytest suite with pytest.ini in subdirectory', () => { const states = await runner.run(config, runner.adapterId); expect(states).to.be.not.empty; - expect(states.map(s => s.test)) + expect(states.map((s) => s.test)) .and.to.include(submoduleTestToRun) .and.to.include(submoduleTestToSkip); - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); }); test('should run suite in submodule', async () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest', - [ - '--ignore=test/import_error_tests' - ]); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest', [ + '--ignore=test/import_error_tests', + ]); const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.be.empty; @@ -326,8 +311,10 @@ suite('Run pytest suite with pytest.ini in subdirectory', () => { const states = await runner.run(config, submoduleSuite.id); expect(states).to.be.not.empty; - expect(states.map(s => s.test)).to.be.lengthOf(1).and.to.include(submoduleTestToRun); - states.forEach(state => { + expect(states.map((s) => s.test)) + .to.be.lengthOf(1) + .and.to.include(submoduleTestToRun); + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); diff --git a/test/tests/pytestGeneral.test.ts b/test/tests/pytestGeneral.test.ts index 8758e84..6d7ff5d 100644 --- a/test/tests/pytestGeneral.test.ts +++ b/test/tests/pytestGeneral.test.ts @@ -10,17 +10,12 @@ import { extractErroredTests, findTestSuiteByLabel, logger, - extractTopLevelLablesAndDescription + extractTopLevelLablesAndDescription, } from '../utils/helpers'; -import { - PYTEST_EXPECTED_SUITES_LIST_WITHOUT_ERRORS, - PYTEST_EXPECTED_SUITES_LIST_WITH_ERRORS -} from '../utils/pytest'; +import { PYTEST_EXPECTED_SUITES_LIST_WITHOUT_ERRORS, PYTEST_EXPECTED_SUITES_LIST_WITH_ERRORS } from '../utils/pytest'; suite('Pytest test discovery with errors', async () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest' - ); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest'); const runner = new PytestTestRunner('some-id', logger()); test('should discover tests with errors', async () => { @@ -32,15 +27,10 @@ suite('Pytest test discovery with errors', async () => { }); suite('Run pytest tests with discovery errors', () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest' - ); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest'); const runner = new PytestTestRunner('some-id', logger()); - [ - 'Error in invalid_syntax_test.py', - 'Error in non_existing_module_test.py' - ].forEach(testMethod => { + ['Error in invalid_syntax_test.py', 'Error in non_existing_module_test.py'].forEach((testMethod) => { test(`should run ${testMethod} test`, async () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; @@ -54,10 +44,10 @@ suite('Run pytest tests with discovery errors', () => { }); suite('Pytest test discovery', async () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest', - ['--doctest-modules', '--ignore=test/import_error_tests'] - ); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest', [ + '--doctest-modules', + '--ignore=test/import_error_tests', + ]); const runner = new PytestTestRunner('some-id', logger()); test('should set runner id on initialization', () => { @@ -86,7 +76,7 @@ suite('Pytest test discovery', async () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.be.empty; - const labels = mainSuite!.children.map(x => x.label); + const labels = mainSuite!.children.map((x) => x.label); expect(labels).to.have.members(PYTEST_EXPECTED_SUITES_LIST_WITHOUT_ERRORS); }); }); @@ -103,7 +93,7 @@ suite('Pytest test discovery with relative cwd folder', async () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.be.empty; - const labels = mainSuite!.children.map(x => x.label); + const labels = mainSuite!.children.map((x) => x.label); expect(labels).to.have.members([ 'describe_test.py', 'env_variables_test.py', @@ -114,16 +104,13 @@ suite('Pytest test discovery with relative cwd folder', async () => { 'subprocess_test.py', 'add_test.py', 'add_test.py', - 'test_simple.py' + 'test_simple.py', ]); }); }); suite('Run pytest tests', () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - 'pytest', - ['--ignore=test/import_error_tests'] - ); + const config: IWorkspaceConfiguration = createPytestConfiguration('pytest', ['--ignore=test/import_error_tests']); const runner = new PytestTestRunner('some-id', logger()); test('should run all tests', async () => { @@ -133,7 +120,7 @@ suite('Run pytest tests', () => { expect(mainSuite!.label).to.be.eq('Pytest tests'); const states = await runner.run(config, runner.adapterId); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -152,14 +139,14 @@ suite('Run pytest tests', () => { { file: 'test/string_test.py', case: '::StringTestCaseOnSameLevelAsFunctions::test_lower_passed', - } + }, ], }, { suite: { label: 'add_test.py', description: 'inner_tests' }, cases: [ { file: 'test/inner_tests/add_test.py', case: '::test_one_plus_two_is_three_passed' }, - { file: 'test/inner_tests/add_test.py', case: '::test_two_plus_two_is_five_failed' } + { file: 'test/inner_tests/add_test.py', case: '::test_two_plus_two_is_five_failed' }, ], }, { @@ -168,7 +155,7 @@ suite('Run pytest tests', () => { { file: 'test/inner_fixture_test.py', case: '::Test_CheckMyApp::Test_NestedClassB::Test_nested_classC_Of_B::test_e_passed', - } + }, ], }, { @@ -177,9 +164,9 @@ suite('Run pytest tests', () => { { file: 'test/describe_test.py', case: '::describe_list::describe_append::adds_to_end_of_list_passed', - } + }, ], - } + }, ].forEach(({ suite, cases }) => { test(`should run ${suite.label} suite`, async () => { const mainSuite = await runner.load(config); @@ -190,10 +177,10 @@ suite('Run pytest tests', () => { const states = await runner.run(config, suiteToRun!.id); expect(states).to.be.not.empty; const cwd = config.getCwd(); - expect(states.map(s => s.test)).to.have.deep.members( - cases.map(c => path.resolve(cwd, c.file) + c.case) + expect(states.map((s) => s.test)).to.have.deep.members( + cases.map((c) => path.resolve(cwd, c.file) + c.case) ); - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -208,8 +195,8 @@ suite('Run pytest tests', () => { 'test_passed[3-a-z]', 'test_nested_class_methodC_passed', 'test_d_passed', - 'removes_item_from_list_passed' - ].forEach(testMethod => { + 'removes_item_from_list_passed', + ].forEach((testMethod) => { test(`should run ${testMethod} test`, async () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; @@ -218,7 +205,7 @@ suite('Run pytest tests', () => { expect(suite).to.be.not.undefined; const states = await runner.run(config, suite!.id); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -244,21 +231,20 @@ suite('Run pytest tests', () => { expect(state.decorations![0].message).to.satisfy((m: string) => m.startsWith('assert (2 + 2) == 5')); }); - [ - 'test_environment_variable_from_env_file_passed', - 'test_environment_variable_from_process_passed' - ].forEach(testMethod => { - test(`should load evironment variables for ${testMethod} test`, async () => { - const mainSuite = await runner.load(config); - expect(mainSuite).to.be.not.undefined; - expect(extractErroredTests(mainSuite!)).to.be.empty; - const suite = findTestSuiteByLabel(mainSuite!, testMethod); - expect(suite).to.be.not.undefined; - const states = await runner.run(config, suite!.id); - expect(states).to.be.not.empty; - states.forEach(state => { - expect(state.state).to.be.eq(extractExpectedState(state.test as string)); + ['test_environment_variable_from_env_file_passed', 'test_environment_variable_from_process_passed'].forEach( + (testMethod) => { + test(`should load evironment variables for ${testMethod} test`, async () => { + const mainSuite = await runner.load(config); + expect(mainSuite).to.be.not.undefined; + expect(extractErroredTests(mainSuite!)).to.be.empty; + const suite = findTestSuiteByLabel(mainSuite!, testMethod); + expect(suite).to.be.not.undefined; + const states = await runner.run(config, suite!.id); + expect(states).to.be.not.empty; + states.forEach((state) => { + expect(state.state).to.be.eq(extractExpectedState(state.test as string)); + }); }); - }); - }); + } + ); }); diff --git a/test/tests/pytestScript.test.ts b/test/tests/pytestScript.test.ts index bf9d39e..c449d06 100644 --- a/test/tests/pytestScript.test.ts +++ b/test/tests/pytestScript.test.ts @@ -7,7 +7,7 @@ import { IPytestConfiguration, ITestplanConfiguration, IUnittestConfiguration, - IWorkspaceConfiguration + IWorkspaceConfiguration, } from '../../src/configuration/workspaceConfiguration'; import { PytestTestRunner } from '../../src/pytest/pytestTestRunner'; import { PlaceholderAwareWorkspaceConfiguration } from '../../src/configuration/placeholderAwareWorkspaceConfiguration'; @@ -18,41 +18,46 @@ import { findTestSuiteByLabel, logger, findWorkspaceFolder, - extractTopLevelLablesAndDescription + extractTopLevelLablesAndDescription, } from '../utils/helpers'; import { PYTEST_EXPECTED_SUITES_LIST_WITH_ERRORS } from '../utils/pytest'; function createPytestConfiguration(args?: string[]): IWorkspaceConfiguration { const wf = findWorkspaceFolder('pytest')!; - return new PlaceholderAwareWorkspaceConfiguration({ - pythonPath(): string { - return getPythonExecutable(); - }, - getCwd(): string { - return wf.uri.fsPath; - }, - envFile(): string { - return path.join(wf.uri.fsPath, '..', '.env'); - }, - autoTestDiscoverOnSaveEnabled(): boolean { - return true; - }, - getUnittestConfiguration(): IUnittestConfiguration { - throw new Error(); - }, - getPytestConfiguration(): IPytestConfiguration { - return { - pytestPath: () => os.platform() === 'win32' ? - '${workspaceFolder}/pytest_runner.bat' : - '${workspaceFolder}/pytest_runner.sh', - isPytestEnabled: true, - pytestArguments: args || [], - }; - }, - getTestplanConfiguration(): ITestplanConfiguration { - throw new Error(); + return new PlaceholderAwareWorkspaceConfiguration( + { + pythonPath(): string { + return getPythonExecutable(); + }, + getCwd(): string { + return wf.uri.fsPath; + }, + envFile(): string { + return path.join(wf.uri.fsPath, '..', '.env'); + }, + autoTestDiscoverOnSaveEnabled(): boolean { + return true; + }, + getUnittestConfiguration(): IUnittestConfiguration { + throw new Error(); + }, + getPytestConfiguration(): IPytestConfiguration { + return { + pytestPath: () => + os.platform() === 'win32' + ? '${workspaceFolder}/pytest_runner.bat' + : '${workspaceFolder}/pytest_runner.sh', + isPytestEnabled: true, + pytestArguments: args || [], + }; + }, + getTestplanConfiguration(): ITestplanConfiguration { + throw new Error(); + }, }, - }, wf, logger()); + wf, + logger() + ); } suite('Pytest test discovery with a script', async () => { @@ -68,9 +73,7 @@ suite('Pytest test discovery with a script', async () => { }); suite('Pytest test execution with a script', () => { - const config: IWorkspaceConfiguration = createPytestConfiguration( - ['--ignore=test/import_error_tests'] - ); + const config: IWorkspaceConfiguration = createPytestConfiguration(['--ignore=test/import_error_tests']); const runner = new PytestTestRunner('some-id', logger()); test('should run all tests', async () => { @@ -80,7 +83,7 @@ suite('Pytest test execution with a script', () => { expect(mainSuite!.label).to.be.eq('Pytest tests'); const states = await runner.run(config, runner.adapterId); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -99,14 +102,14 @@ suite('Pytest test execution with a script', () => { { file: 'test/string_test.py', case: '::StringTestCaseOnSameLevelAsFunctions::test_lower_passed', - } + }, ], }, { suite: { label: 'add_test.py', description: 'inner_tests' }, cases: [ { file: 'test/inner_tests/add_test.py', case: '::test_one_plus_two_is_three_passed' }, - { file: 'test/inner_tests/add_test.py', case: '::test_two_plus_two_is_five_failed' } + { file: 'test/inner_tests/add_test.py', case: '::test_two_plus_two_is_five_failed' }, ], }, { @@ -115,7 +118,7 @@ suite('Pytest test execution with a script', () => { { file: 'test/inner_fixture_test.py', case: '::Test_CheckMyApp::Test_NestedClassB::Test_nested_classC_Of_B::test_e_passed', - } + }, ], }, { @@ -124,9 +127,9 @@ suite('Pytest test execution with a script', () => { { file: 'test/describe_test.py', case: '::describe_list::describe_append::adds_to_end_of_list_passed', - } + }, ], - } + }, ].forEach(({ suite, cases }) => { test(`should run ${suite.label} suite`, async () => { const mainSuite = await runner.load(config); @@ -137,10 +140,10 @@ suite('Pytest test execution with a script', () => { const states = await runner.run(config, suiteToRun!.id); expect(states).to.be.not.empty; const cwd = config.getCwd(); - expect(states.map(s => s.test)).to.have.deep.members( - cases.map(c => path.resolve(cwd, c.file) + c.case) + expect(states.map((s) => s.test)).to.have.deep.members( + cases.map((c) => path.resolve(cwd, c.file) + c.case) ); - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -155,8 +158,8 @@ suite('Pytest test execution with a script', () => { 'test_passed[3-a-z]', 'test_nested_class_methodC_passed', 'test_d_passed', - 'removes_item_from_list_passed' - ].forEach(testMethod => { + 'removes_item_from_list_passed', + ].forEach((testMethod) => { test(`should run ${testMethod} test`, async () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; @@ -165,7 +168,7 @@ suite('Pytest test execution with a script', () => { expect(suite).to.be.not.undefined; const states = await runner.run(config, suite!.id); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -191,21 +194,20 @@ suite('Pytest test execution with a script', () => { expect(state.decorations![0].message).to.satisfy((m: string) => m.startsWith('assert (2 + 2) == 5')); }).timeout(60000); - [ - 'test_environment_variable_from_env_file_passed', - 'test_environment_variable_from_process_passed' - ].forEach(testMethod => { - test(`should load evironment variables for ${testMethod} test`, async () => { - const mainSuite = await runner.load(config); - expect(mainSuite).to.be.not.undefined; - expect(extractErroredTests(mainSuite!)).to.be.empty; - const suite = findTestSuiteByLabel(mainSuite!, testMethod); - expect(suite).to.be.not.undefined; - const states = await runner.run(config, suite!.id); - expect(states).to.be.not.empty; - states.forEach(state => { - expect(state.state).to.be.eq(extractExpectedState(state.test as string)); - }); - }).timeout(60000); - }); + ['test_environment_variable_from_env_file_passed', 'test_environment_variable_from_process_passed'].forEach( + (testMethod) => { + test(`should load evironment variables for ${testMethod} test`, async () => { + const mainSuite = await runner.load(config); + expect(mainSuite).to.be.not.undefined; + expect(extractErroredTests(mainSuite!)).to.be.empty; + const suite = findTestSuiteByLabel(mainSuite!, testMethod); + expect(suite).to.be.not.undefined; + const states = await runner.run(config, suite!.id); + expect(states).to.be.not.empty; + states.forEach((state) => { + expect(state.state).to.be.eq(extractExpectedState(state.test as string)); + }); + }).timeout(60000); + } + ); }); diff --git a/test/tests/pythonTestAdapter.test.ts b/test/tests/pythonTestAdapter.test.ts index bf83d29..6918615 100644 --- a/test/tests/pythonTestAdapter.test.ts +++ b/test/tests/pythonTestAdapter.test.ts @@ -16,7 +16,7 @@ import { extractErroredTests, findTestSuiteByLabel, findWorkspaceFolder, - logger + logger, } from '../utils/helpers'; [ @@ -27,38 +27,27 @@ import { testsToRun: [ 'test_basic_two_plus_one_is_three_passed', 'test_basic_two_plus_two_is_five_failed', - 'test_basic_two_plus_zero_is_two_skipped' + 'test_basic_two_plus_zero_is_two_skipped', ], suiteToSort: { suite: { label: 'AddTests', description: 'basic_tests.test_add' }, sortedTests: [ 'test_basic_two_plus_one_is_three_passed', 'test_basic_two_plus_two_is_five_failed', - 'test_basic_two_plus_zero_is_two_skipped' + 'test_basic_two_plus_zero_is_two_skipped', ], }, }, { label: 'pytest', runner: new PytestTestRunner('second-id', logger()), - configuration: createPytestConfiguration( - 'pytest', - ['--ignore=test/import_error_tests'] - ), - testsToRun: [ - 'test_one_plus_two_is_three_passed', - 'test_two_plus_two_is_five_failed', - 'test_capitalize_passed' - ], + configuration: createPytestConfiguration('pytest', ['--ignore=test/import_error_tests']), + testsToRun: ['test_one_plus_two_is_three_passed', 'test_two_plus_two_is_five_failed', 'test_capitalize_passed'], suiteToSort: { suite: { label: 'TestSampleWithScenarios' }, - sortedTests: [ - 'test_demo1_passed', - 'test_demo2_passed', - 'test_demo10_passed' - ], + sortedTests: ['test_demo1_passed', 'test_demo2_passed', 'test_demo10_passed'], }, - } + }, ].forEach(({ label, runner, configuration, testsToRun, suiteToSort }) => { suite(`Adapter events with ${label} runner`, () => { const workspaceFolder = findWorkspaceFolder(label)!; @@ -73,7 +62,7 @@ import { let startedNotifications = 0; let finishedNotifications = 0; let finishedEvent: TestLoadFinishedEvent | undefined; - adapter.tests(event => { + adapter.tests((event) => { if (event.type === 'started') { startedNotifications++; } else { @@ -96,12 +85,12 @@ import { const mainSuite = await runner.load(await configurationFactory.get(workspaceFolder)); // expect(errors).to.be.empty; expect(mainSuite).to.be.not.undefined; - const suites = testsToRun.map(t => findTestSuiteByLabel(mainSuite!, t)!); + const suites = testsToRun.map((t) => findTestSuiteByLabel(mainSuite!, t)!); let startedNotifications = 0; let finishedNotifications = 0; const states: TestEvent[] = []; - adapter.testStates(event => { + adapter.testStates((event) => { if (event.type === 'started') { startedNotifications++; } else if (event.type === 'finished') { @@ -112,14 +101,14 @@ import { /* */ } }); - await adapter.run(suites.map(s => s.id)); + await adapter.run(suites.map((s) => s.id)); expect(startedNotifications).to.be.eq(1); expect(startedNotifications).to.be.eq(finishedNotifications); expect(states).to.be.not.empty; expect(states).to.have.length(testsToRun.length); - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -130,7 +119,7 @@ import { let startedNotifications = 0; let finishedNotifications = 0; let finishedEvent: TestLoadFinishedEvent | undefined; - adapter.tests(event => { + adapter.tests((event) => { if (event.type === 'started') { startedNotifications++; } else { @@ -150,42 +139,31 @@ import { const suiteToCheck = findTestSuiteByLabel( finishedEvent!.suite!, suiteToSort.suite.label, - suiteToSort.suite.description)! as TestSuiteInfo; + suiteToSort.suite.description + )! as TestSuiteInfo; expect(suiteToCheck.type).to.be.eq('suite'); expect(suiteToCheck.children).to.be.not.empty; - expect(suiteToCheck.children.map(t => t.label)).to.have.ordered.members(suiteToSort.sortedTests); + expect(suiteToCheck.children.map((t) => t.label)).to.have.ordered.members(suiteToSort.sortedTests); }); }); }); suite('Adapter events with pytest runner and invalid files during discovery', () => { - const testsToRun = [ - 'Error in invalid_syntax_test.py', - 'Error in non_existing_module_test.py' - ]; + const testsToRun = ['Error in invalid_syntax_test.py', 'Error in non_existing_module_test.py']; const workspaceFolder = findWorkspaceFolder('pytest')!; const configurationFactory: IConfigurationFactory = { get(_: vscode.WorkspaceFolder): Promise { - return Promise.resolve( - createPytestConfiguration( - 'pytest' - ) - ); + return Promise.resolve(createPytestConfiguration('pytest')); }, }; const runner = new PytestTestRunner('some-id', logger()); - const adapter = new PythonTestAdapter( - workspaceFolder, - runner, - configurationFactory, - logger() - ); + const adapter = new PythonTestAdapter(workspaceFolder, runner, configurationFactory, logger()); test('discovery events should be successfully fired', async () => { let startedNotifications = 0; let finishedNotifications = 0; let finishedEvent: TestLoadFinishedEvent | undefined; - adapter.tests(event => { + adapter.tests((event) => { if (event.type === 'started') { startedNotifications++; } else { @@ -207,12 +185,12 @@ suite('Adapter events with pytest runner and invalid files during discovery', () const mainSuite = await runner.load(await configurationFactory.get(workspaceFolder)); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.have.length(2); - const suites = testsToRun.map(t => findTestSuiteByLabel(mainSuite!, t)!); + const suites = testsToRun.map((t) => findTestSuiteByLabel(mainSuite!, t)!); let startedNotifications = 0; let finishedNotifications = 0; const states: TestEvent[] = []; - adapter.testStates(event => { + adapter.testStates((event) => { if (event.type === 'started') { startedNotifications++; } else if (event.type === 'finished') { @@ -223,14 +201,14 @@ suite('Adapter events with pytest runner and invalid files during discovery', () /* */ } }); - await adapter.run(suites.map(s => s.id)); + await adapter.run(suites.map((s) => s.id)); expect(startedNotifications).to.be.eq(1); expect(startedNotifications).to.be.eq(finishedNotifications); expect(states).to.be.not.empty; expect(states).to.have.length(testsToRun.length); - expect(states.map(s => ({ state: s.state, id: s.test }))).to.have.deep.members([ + expect(states.map((s) => ({ state: s.state, id: s.test }))).to.have.deep.members([ { state: 'failed', id: path.join(workspaceFolder.uri.fsPath, 'test', 'import_error_tests', 'invalid_syntax_test.py'), @@ -238,40 +216,27 @@ suite('Adapter events with pytest runner and invalid files during discovery', () { state: 'failed', id: path.join(workspaceFolder.uri.fsPath, 'test', 'import_error_tests', 'non_existing_module_test.py'), - } + }, ]); }); }); suite('Adapter events with unittest runner and invalid files during discovery', () => { - const testsToRun = [ - 'test_invalid_syntax_failed', - 'InvalidTestIdTests_failed', - 'test_invalid_import_failed' - ]; + const testsToRun = ['test_invalid_syntax_failed', 'InvalidTestIdTests_failed', 'test_invalid_import_failed']; const workspaceFolder = findWorkspaceFolder('unittest')!; const configurationFactory: IConfigurationFactory = { get(_: vscode.WorkspaceFolder): Promise { - return Promise.resolve( - createUnittestConfiguration( - 'unittest' - ) - ); + return Promise.resolve(createUnittestConfiguration('unittest')); }, }; const runner = new UnittestTestRunner('some-id', logger()); - const adapter = new PythonTestAdapter( - workspaceFolder, - runner, - configurationFactory, - logger() - ); + const adapter = new PythonTestAdapter(workspaceFolder, runner, configurationFactory, logger()); test('discovery events should be successfully fired', async () => { let startedNotifications = 0; let finishedNotifications = 0; let finishedEvent: TestLoadFinishedEvent | undefined; - adapter.tests(event => { + adapter.tests((event) => { if (event.type === 'started') { startedNotifications++; } else { @@ -293,12 +258,12 @@ suite('Adapter events with unittest runner and invalid files during discovery', const mainSuite = await runner.load(await configurationFactory.get(workspaceFolder)); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.have.length(3); - const suites = testsToRun.map(t => findTestSuiteByLabel(mainSuite!, t)!); + const suites = testsToRun.map((t) => findTestSuiteByLabel(mainSuite!, t)!); let startedNotifications = 0; let finishedNotifications = 0; const states: TestEvent[] = []; - adapter.testStates(event => { + adapter.testStates((event) => { if (event.type === 'started') { startedNotifications++; } else if (event.type === 'finished') { @@ -309,14 +274,14 @@ suite('Adapter events with unittest runner and invalid files during discovery', /* */ } }); - await adapter.run(suites.map(s => s.id)); + await adapter.run(suites.map((s) => s.id)); expect(startedNotifications).to.be.eq(1); expect(startedNotifications).to.be.eq(finishedNotifications); expect(states).to.be.not.empty; expect(states).to.have.length(testsToRun.length); - expect(states.map(s => ({ state: s.state, id: s.test }))).to.have.deep.members([ + expect(states.map((s) => ({ state: s.state, id: s.test }))).to.have.deep.members([ { state: 'failed', id: 'invalid_tests.test_invalid_syntax_failed', @@ -328,7 +293,7 @@ suite('Adapter events with unittest runner and invalid files during discovery', { state: 'failed', id: 'test_invalid_import_failed', - } + }, ]); }); }); diff --git a/test/tests/testCancellation.test.ts b/test/tests/testCancellation.test.ts index 1c6643f..d2a52e0 100644 --- a/test/tests/testCancellation.test.ts +++ b/test/tests/testCancellation.test.ts @@ -14,7 +14,7 @@ import { extractErroredTests, findTestSuiteByLabel, logger, - sleep + sleep, } from '../utils/helpers'; import { isTestplanPrerequisiteMet } from './utilities'; @@ -30,10 +30,9 @@ import { isTestplanPrerequisiteMet } from './utilities'; runner: new PytestTestRunner('second-id', logger()), configuration: createPytestConfiguration('pytest_test_cancellation'), allowNoTestCompleted: os.platform() === 'win32', - } + }, ].forEach(({ label, runner, configuration, allowNoTestCompleted }) => { suite(`Test cancellation with ${label}`, () => { - test('should run and cancel all tests', async () => { const mainSuite = await runner.load(configuration); expect(mainSuite).to.be.not.undefined; @@ -69,7 +68,7 @@ import { isTestplanPrerequisiteMet } from './utilities'; }); }); -isTestplanPrerequisiteMet().then(isTestplan => { +isTestplanPrerequisiteMet().then((isTestplan) => { if (isTestplan) { // FIXME: These tests were instable (exceeding timeout of 60s) on macOS suite.skip('Test cancellation with testplan', async () => { @@ -104,4 +103,4 @@ isTestplanPrerequisiteMet().then(isTestplan => { }); }); } -}); \ No newline at end of file +}); diff --git a/test/tests/testplanGeneral.test.ts b/test/tests/testplanGeneral.test.ts index ed26e0b..5eabd6c 100644 --- a/test/tests/testplanGeneral.test.ts +++ b/test/tests/testplanGeneral.test.ts @@ -9,18 +9,16 @@ import { extractAllIds, extractExpectedState, findTestSuiteByLabel, - logger + logger, } from '../utils/helpers'; import { isTestplanPrerequisiteMet } from './utilities'; -isTestplanPrerequisiteMet().then(isTestplan => { +isTestplanPrerequisiteMet().then((isTestplan) => { if (!isTestplan) { return; } suite('Testplan test discovery', async () => { - const config: IWorkspaceConfiguration = createTestplanConfiguration( - 'testplan' - ); + const config: IWorkspaceConfiguration = createTestplanConfiguration('testplan'); const runner = new TestplanTestRunner('some-id', logger()); test('should set runner id on initialization', () => { @@ -49,20 +47,20 @@ isTestplanPrerequisiteMet().then(isTestplan => { expect(mainSuite).to.be.not.undefined; const expectedHierarchy = { - 'Primary': { - 'AlphaSuite': { - 'test_equality_passed': {}, - 'test_equality_failed': {}, - 'test_membership_passed': {}, - 'test_membership_failed': {}, - 'test_regex_passed': {}, - 'test_regex_failed': {}, + Primary: { + AlphaSuite: { + test_equality_passed: {}, + test_equality_failed: {}, + test_membership_passed: {}, + test_membership_failed: {}, + test_regex_passed: {}, + test_regex_failed: {}, }, }, - 'Secondary': { - 'BetaSuite': { - 'testcase_one_passed': {}, - 'testcase_two_passed': {}, + Secondary: { + BetaSuite: { + testcase_one_passed: {}, + testcase_two_passed: {}, }, }, }; @@ -81,37 +79,27 @@ isTestplanPrerequisiteMet().then(isTestplan => { 'Secondary', 'Secondary:BetaSuite', 'Secondary:BetaSuite:testcase_one_passed', - 'Secondary:BetaSuite:testcase_two_passed' + 'Secondary:BetaSuite:testcase_two_passed', ]); }); }).timeout(60000); suite('Testplan test discovery with relative cwd folder', async () => { - const config: IWorkspaceConfiguration = createTestplanConfiguration( - 'testplan', - [], - 'basic' - ); + const config: IWorkspaceConfiguration = createTestplanConfiguration('testplan', [], 'basic'); const runner = new TestplanTestRunner('some-id', logger()); test('should discover tests', async () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; - expectLabelsAreSameRecursive( - { 'TestEcho': { 'MyTestsuite': { 'my_testcase': {} } } }, - mainSuite!); + expectLabelsAreSameRecursive({ TestEcho: { MyTestsuite: { my_testcase: {} } } }, mainSuite!); const ids = extractAllIds(mainSuite!); - expect(ids).to.have.deep.members( - ['TestEcho', 'TestEcho:MyTestsuite', 'TestEcho:MyTestsuite:my_testcase'] - ); + expect(ids).to.have.deep.members(['TestEcho', 'TestEcho:MyTestsuite', 'TestEcho:MyTestsuite:my_testcase']); }); }).timeout(60000); suite('Run testplan tests', () => { - const config: IWorkspaceConfiguration = createTestplanConfiguration( - 'testplan' - ); + const config: IWorkspaceConfiguration = createTestplanConfiguration('testplan'); const runner = new TestplanTestRunner('some-id', logger()); test('should run all tests', async () => { @@ -120,7 +108,7 @@ isTestplanPrerequisiteMet().then(isTestplan => { expect(mainSuite!.label).to.be.eq('Testplan tests'); const states = await runner.run(config, runner.adapterId); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -135,16 +123,16 @@ isTestplanPrerequisiteMet().then(isTestplan => { { file: 'test/test_plan.py', case: 'Primary:AlphaSuite:test_membership_passed' }, { file: 'test/test_plan.py', case: 'Primary:AlphaSuite:test_membership_failed' }, { file: 'test/test_plan.py', case: 'Primary:AlphaSuite:test_regex_passed' }, - { file: 'test/test_plan.py', case: 'Primary:AlphaSuite:test_regex_failed' } + { file: 'test/test_plan.py', case: 'Primary:AlphaSuite:test_regex_failed' }, ], }, { suite: { label: 'BetaSuite', description: undefined }, cases: [ { file: 'test/test_plan.py', case: 'Secondary:BetaSuite:testcase_one_passed' }, - { file: 'test/test_plan.py', case: 'Secondary:BetaSuite:testcase_two_passed' } + { file: 'test/test_plan.py', case: 'Secondary:BetaSuite:testcase_two_passed' }, ], - } + }, ].forEach(({ suite, cases }) => { test(`should run ${suite.label} suite`, async () => { const mainSuite = await runner.load(config); @@ -153,10 +141,8 @@ isTestplanPrerequisiteMet().then(isTestplan => { expect(suiteToRun).to.be.not.undefined; const states = await runner.run(config, suiteToRun!.id); expect(states).to.be.not.empty; - expect(states.map(s => s.test)).to.have.deep.members( - cases.map(c => c.case) - ); - states.forEach(state => { + expect(states.map((s) => s.test)).to.have.deep.members(cases.map((c) => c.case)); + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); diff --git a/test/tests/unittestGeneral.test.ts b/test/tests/unittestGeneral.test.ts index 6eb4bad..083caef 100644 --- a/test/tests/unittestGeneral.test.ts +++ b/test/tests/unittestGeneral.test.ts @@ -4,7 +4,13 @@ import * as vscode from 'vscode'; import { IWorkspaceConfiguration } from '../../src/configuration/workspaceConfiguration'; import { UnittestTestRunner } from '../../src/unittest/unittestTestRunner'; -import { createUnittestConfiguration, extractExpectedState, extractErroredTests, findTestSuiteByLabel, logger } from '../utils/helpers'; +import { + createUnittestConfiguration, + extractExpectedState, + extractErroredTests, + findTestSuiteByLabel, + logger, +} from '../utils/helpers'; suite('Unittest test discovery', () => { const config: IWorkspaceConfiguration = createUnittestConfiguration('unittest'); @@ -47,9 +53,9 @@ suite('Unittest test discovery', () => { 'InvalidTestIdTests_failed', 'test_invalid_import_failed', 'test_invalid_syntax_failed', - 'StringTestWithSimilarNames' + 'StringTestWithSimilarNames', ]; - const labels = mainSuite!.children.map(x => x.label); + const labels = mainSuite!.children.map((x) => x.label); expect(labels).to.have.members(expectedSuites); }); @@ -65,7 +71,7 @@ suite('Unittest test discovery', () => { { testId: 'invalid_tests.test_invalid_test_id.InvalidTestIdTests_failed', error: /Failed to get test id: invalid_tests.test_invalid_test_id.InvalidTestIdTests_failed/, - } + }, ].forEach(({ testId, error }) => { test(`should show errors for invalid test ${testId}`, async () => { const mainSuite = await runner.load(config); @@ -73,8 +79,8 @@ suite('Unittest test discovery', () => { const errors = extractErroredTests(mainSuite!); expect(errors).to.be.not.empty; - expect(errors.map(x => x.id)).to.contain(testId); - const invalidTest = errors.filter(x => x.id === testId)[0]; + expect(errors.map((x) => x.id)).to.contain(testId); + const invalidTest = errors.filter((x) => x.id === testId)[0]; expect(invalidTest.message).to.match(error); }); }); @@ -91,7 +97,7 @@ suite('Run unittest tests', () => { expect(mainSuite!.label).to.be.eq('Unittest tests'); const states = await runner.run(config, mainSuite!.id); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -100,7 +106,7 @@ suite('Run unittest tests', () => { [ { testCase: 'TestWithOutputBeforeImport' }, { testCase: 'TestWithSetUpClassMethod' }, - { testCase: 'AddTests', description: 'basic_tests.test_add' } + { testCase: 'AddTests', description: 'basic_tests.test_add' }, ].forEach(({ testCase, description }) => { test(`should run ${testCase} suite`, async () => { const mainSuite = await runner.load(config); @@ -110,7 +116,7 @@ suite('Run unittest tests', () => { expect(suite).to.be.not.undefined; const states = await runner.run(config, suite!.id); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -122,8 +128,8 @@ suite('Run unittest tests', () => { 'test_basic_two_plus_two_is_five_failed', 'test_basic_two_plus_zero_is_two_skipped', 'test_set_up_called_before_test_case1_passed', - 'test_set_up_called_before_test_case2_passed' - ].forEach(testMethod => { + 'test_set_up_called_before_test_case2_passed', + ].forEach((testMethod) => { test(`should run ${testMethod} test`, async () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; @@ -132,17 +138,14 @@ suite('Run unittest tests', () => { expect(suite).to.be.not.undefined; const states = await runner.run(config, suite!.id); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); }); }); - [ - 'test_basic_two_plus_one_is_three_passed', - 'test_basic_two_plus_two_is_five_failed' - ].forEach(testMethod => { + ['test_basic_two_plus_one_is_three_passed', 'test_basic_two_plus_two_is_five_failed'].forEach((testMethod) => { test(`should capture output from ${testMethod} test`, async () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; @@ -151,7 +154,7 @@ suite('Run unittest tests', () => { expect(suite).to.be.not.undefined; const states = await runner.run(config, suite!.id); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); expect(state.message).to.be.not.empty; @@ -186,8 +189,7 @@ suite('Unittest run and discovery with start folder in config', () => { return 'python'; }, getCwd(): string { - const folders = vscode.workspace.workspaceFolders! - .filter(f => f.name === 'unittest'); + const folders = vscode.workspace.workspaceFolders!.filter((f) => f.name === 'unittest'); return folders[0].uri.fsPath; }, envFile(): string { @@ -218,10 +220,8 @@ suite('Unittest run and discovery with start folder in config', () => { const mainSuite = await runner.load(config); expect(mainSuite).to.be.not.undefined; expect(extractErroredTests(mainSuite!)).to.be.empty; - const expectedSuites = [ - 'AddTestsWithoutInit' - ]; - const labels = mainSuite!.children.map(x => x.label); + const expectedSuites = ['AddTestsWithoutInit']; + const labels = mainSuite!.children.map((x) => x.label); expect(labels).to.have.members(expectedSuites); }); @@ -232,7 +232,7 @@ suite('Unittest run and discovery with start folder in config', () => { expect(mainSuite!.label).to.be.eq('Unittest tests'); const states = await runner.run(config, mainSuite!.id); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -246,7 +246,7 @@ suite('Unittest run and discovery with start folder in config', () => { expect(suite).to.be.not.undefined; const states = await runner.run(config, suite!.id); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); @@ -260,7 +260,7 @@ suite('Unittest run and discovery with start folder in config', () => { expect(suite).to.be.not.undefined; const states = await runner.run(config, suite!.id); expect(states).to.be.not.empty; - states.forEach(state => { + states.forEach((state) => { const expectedState = extractExpectedState(state.test as string); expect(state.state).to.be.eq(expectedState); }); diff --git a/test/tests/unittestSuitParser.test.ts b/test/tests/unittestSuitParser.test.ts index 21fbcba..801995d 100644 --- a/test/tests/unittestSuitParser.test.ts +++ b/test/tests/unittestSuitParser.test.ts @@ -42,13 +42,15 @@ suite('Unittest suite parser', () => { label: expectedSuitLabel, file: path.join(prefixPath, 'some_test_module.py'), tooltip: expectedSuitId, - children: [{ - type: 'test', - id: expectedTestId, - file: path.join(prefixPath, 'some_test_module.py'), - label: expectedTestLabel, - tooltip: expectedTestId, - }], + children: [ + { + type: 'test', + id: expectedTestId, + file: path.join(prefixPath, 'some_test_module.py'), + label: expectedTestLabel, + tooltip: expectedTestId, + }, + ], }); }); @@ -56,13 +58,15 @@ suite('Unittest suite parser', () => { const prefixPath = path.resolve('/some/prefix/path'); const expectedSuitLabel = 'TestCase1'; const expectedSuitId = 'some_test_module.' + expectedSuitLabel; - const expectedTests = ['test_function1', 'test_function1'].map(label => ({ + const expectedTests = ['test_function1', 'test_function1'].map((label) => ({ id: expectedSuitId + '.' + label, label, })); const suites = parseTestSuites( - '==DISCOVERED TESTS BEGIN=={ "tests": [' + expectedTests.map(t => (`{ "id": "${t.id}" }`)).join(',\n') + '], "errors": [] }==DISCOVERED TESTS END==', + '==DISCOVERED TESTS BEGIN=={ "tests": [' + + expectedTests.map((t) => `{ "id": "${t.id}" }`).join(',\n') + + '], "errors": [] }==DISCOVERED TESTS END==', prefixPath ); expect(suites).to.have.length(1); @@ -77,7 +81,7 @@ suite('Unittest suite parser', () => { label: expectedSuitLabel, file: path.join(prefixPath, 'some_test_module.py'), tooltip: expectedSuitId, - children: expectedTests.map(test => ({ + children: expectedTests.map((test) => ({ type: 'test', id: test.id, file: path.join(prefixPath, 'some_test_module.py'), @@ -91,13 +95,15 @@ suite('Unittest suite parser', () => { const prefixPath = '/some/prefix/path'; const expectedSuitLabel = 'TestCase1'; const expectedSuitId = expectedSuitLabel; - const expectedTests = ['test_function1', 'test_function1'].map(label => ({ + const expectedTests = ['test_function1', 'test_function1'].map((label) => ({ id: expectedSuitId + '.' + label, label, })); const suites = parseTestSuites( - '==DISCOVERED TESTS BEGIN=={ "tests": [' + expectedTests.map(t => (`{ "id": "${t.id}" }`)).join(',\n') + '], "errors": [] }==DISCOVERED TESTS END==', + '==DISCOVERED TESTS BEGIN=={ "tests": [' + + expectedTests.map((t) => `{ "id": "${t.id}" }`).join(',\n') + + '], "errors": [] }==DISCOVERED TESTS END==', prefixPath ); expect(extractErroredTestsFromArray(suites)).to.be.empty; @@ -112,7 +118,7 @@ suite('Unittest suite parser', () => { label: expectedSuitLabel, file: undefined, tooltip: expectedSuitId, - children: expectedTests.map(test => ({ + children: expectedTests.map((test) => ({ type: 'test', id: test.id, file: undefined, @@ -132,48 +138,44 @@ suite('Unittest test states parser', () => { test('should return events for different states', () => { const testOutput = [ 'TEST_EXECUTION_RESULT:failed:' + - 'some_module.TestCase1.test_function1:c29tZSBtdWx0aWxpbmUKZXJyb3IgbWVzc2FnZQ==', + 'some_module.TestCase1.test_function1:c29tZSBtdWx0aWxpbmUKZXJyb3IgbWVzc2FnZQ==', 'TEST_EXECUTION_RESULT:passed:some_module.TestCase1.test_function2', 'TEST_EXECUTION_RESULT:skipped:some_module.TestCase1.test_function3', - 'TEST_EXECUTION_RESULT:passed:some_other_module.TestCase2.test_function' + 'TEST_EXECUTION_RESULT:passed:some_other_module.TestCase2.test_function', ]; const states = parseTestStates(testOutput.join('\n')); expect(states).to.be.not.empty; - expect(states).to.have.deep.members( - [ - { - type: 'test', - state: 'failed', - test: 'some_module.TestCase1.test_function1', - message: `some multiline + expect(states).to.have.deep.members([ + { + type: 'test', + state: 'failed', + test: 'some_module.TestCase1.test_function1', + message: `some multiline error message`, - }, - { - type: 'test', - state: 'passed', - test: 'some_module.TestCase1.test_function2', - message: undefined, - }, - { - type: 'test', - state: 'skipped', - test: 'some_module.TestCase1.test_function3', - message: undefined, - }, - { - type: 'test', - state: 'passed', - test: 'some_other_module.TestCase2.test_function', - message: undefined, - } - ] - ); + }, + { + type: 'test', + state: 'passed', + test: 'some_module.TestCase1.test_function2', + message: undefined, + }, + { + type: 'test', + state: 'skipped', + test: 'some_module.TestCase1.test_function3', + message: undefined, + }, + { + type: 'test', + state: 'passed', + test: 'some_other_module.TestCase2.test_function', + message: undefined, + }, + ]); }); test('should not fail when output is not formatted', () => { - const testOutput = [ - 'Error! Some severe error occurred and output is not readable!' - ]; + const testOutput = ['Error! Some severe error occurred and output is not readable!']; const states = parseTestStates(testOutput.join('\n')); expect(states).to.be.empty; }); diff --git a/test/tests/utilities.test.ts b/test/tests/utilities.test.ts index 0117e91..f4f1325 100644 --- a/test/tests/utilities.test.ts +++ b/test/tests/utilities.test.ts @@ -13,7 +13,7 @@ suite('Description for equal labels', async () => { { id: 'some.module2.SomeTest2', label: 'SomeTest2', - } + }, ]; setDescriptionForEqualLabels(suites, '.'); @@ -25,7 +25,7 @@ suite('Description for equal labels', async () => { { id: 'some.module2.SomeTest2', label: 'SomeTest2', - } + }, ]); }); @@ -38,7 +38,7 @@ suite('Description for equal labels', async () => { { id: 'some.module2.SomeTest', label: 'SomeTest', - } + }, ]; setDescriptionForEqualLabels(suites, '.'); @@ -52,7 +52,7 @@ suite('Description for equal labels', async () => { id: 'some.module2.SomeTest', label: 'SomeTest', description: 'module2', - } + }, ]); }); @@ -69,7 +69,7 @@ suite('Description for equal labels', async () => { { id: 'some.module3.SomeTest', label: 'Error in module3.py', - } + }, ]; setDescriptionForEqualLabels(suites, '.'); @@ -87,7 +87,7 @@ suite('Description for equal labels', async () => { { id: 'some.module3.SomeTest', label: 'Error in module3.py', - } + }, ]); }); }); @@ -116,4 +116,4 @@ suite('String utilities - concatNonEmpty', async () => { test('should return empty string for empty strings', async () => { expect(concatNonEmpty('.', '', '', '', '')).to.be.empty; }); -}); \ No newline at end of file +}); diff --git a/test/tests/vscodeWorkspaceConfiguration.test.ts b/test/tests/vscodeWorkspaceConfiguration.test.ts index b3b467a..0aef76e 100644 --- a/test/tests/vscodeWorkspaceConfiguration.test.ts +++ b/test/tests/vscodeWorkspaceConfiguration.test.ts @@ -17,9 +17,7 @@ suite('VSCode workspace configuration', () => { expect(configuration.getUnittestConfiguration().isUnittestEnabled).to.be.eq( defaults.get('python.unitTest.unittestEnabled', false) ); - expect(configuration.pythonPath()).to.be.eq( - defaults.get('python.pythonPath', 'python') - ); + expect(configuration.pythonPath()).to.be.eq(defaults.get('python.pythonPath', 'python')); expect(configuration.getCwd()).to.be.eq( defaults.get('python.unitTest.cwd') || findWorkspaceFolder('empty_configuration')!.uri.fsPath ); diff --git a/test/tslint.json b/test/tslint.json index 26ffba6..fa44aae 100644 --- a/test/tslint.json +++ b/test/tslint.json @@ -1,7 +1,5 @@ { - "extends": [ - "../tslint.json" - ], + "extends": ["../tslint.json"], "rules": { "no-unused-expression": false, "no-console": false diff --git a/test/utils/helpers.ts b/test/utils/helpers.ts index 172c603..475a996 100644 --- a/test/utils/helpers.ts +++ b/test/utils/helpers.ts @@ -8,7 +8,7 @@ import { IPytestConfiguration, ITestplanConfiguration, IUnittestConfiguration, - IWorkspaceConfiguration + IWorkspaceConfiguration, } from '../../src/configuration/workspaceConfiguration'; import { ILogger } from '../../src/logging/logger'; import { empty } from '../../src/utilities/collections'; @@ -58,7 +58,6 @@ export function findTestSuiteByLabel( label: string, description?: string ): TestSuiteInfo | TestInfo | undefined { - if (suite.label === label) { if (description) { if (suite.description === description) { @@ -83,105 +82,117 @@ export function findTestSuiteByLabel( } export function findWorkspaceFolder(folder: string): vscode.WorkspaceFolder | undefined { - return vscode.workspace.workspaceFolders!.find(f => f.name === folder); + return vscode.workspace.workspaceFolders!.find((f) => f.name === folder); } export function createPytestConfiguration(folder: string, args?: string[], cwd?: string): IWorkspaceConfiguration { const python = getPythonExecutable(); const wf = findWorkspaceFolder(folder)!; - return new PlaceholderAwareWorkspaceConfiguration({ - pythonPath(): string { - return python; - }, - getCwd(): string { - return cwd || wf.uri.fsPath; - }, - envFile(): string { - return path.join(wf.uri.fsPath, '..', '.env'); - }, - autoTestDiscoverOnSaveEnabled(): boolean { - return true; - }, - getUnittestConfiguration(): IUnittestConfiguration { - throw new Error(); - }, - getPytestConfiguration(): IPytestConfiguration { - return { - pytestPath: () => 'pytest', - isPytestEnabled: true, - pytestArguments: args || [], - }; - }, - getTestplanConfiguration(): ITestplanConfiguration { - throw new Error(); - }, - }, wf, logger()); + return new PlaceholderAwareWorkspaceConfiguration( + { + pythonPath(): string { + return python; + }, + getCwd(): string { + return cwd || wf.uri.fsPath; + }, + envFile(): string { + return path.join(wf.uri.fsPath, '..', '.env'); + }, + autoTestDiscoverOnSaveEnabled(): boolean { + return true; + }, + getUnittestConfiguration(): IUnittestConfiguration { + throw new Error(); + }, + getPytestConfiguration(): IPytestConfiguration { + return { + pytestPath: () => 'pytest', + isPytestEnabled: true, + pytestArguments: args || [], + }; + }, + getTestplanConfiguration(): ITestplanConfiguration { + throw new Error(); + }, + }, + wf, + logger() + ); } export function createUnittestConfiguration(folder: string): IWorkspaceConfiguration { const python = getPythonExecutable(); const wf = findWorkspaceFolder(folder)!; - return new PlaceholderAwareWorkspaceConfiguration({ - pythonPath(): string { - return python; - }, - getCwd(): string { - return wf.uri.fsPath; - }, - envFile(): string { - return path.join(wf.uri.fsPath, '.env'); - }, - autoTestDiscoverOnSaveEnabled(): boolean { - return true; - }, - getUnittestConfiguration(): IUnittestConfiguration { - return { - isUnittestEnabled: true, - unittestArguments: { - startDirectory: '.', - pattern: 'test_*.py', - }, - }; - }, - getPytestConfiguration(): IPytestConfiguration { - throw new Error(); - }, - getTestplanConfiguration(): ITestplanConfiguration { - throw new Error(); - }, - }, wf, logger()); + return new PlaceholderAwareWorkspaceConfiguration( + { + pythonPath(): string { + return python; + }, + getCwd(): string { + return wf.uri.fsPath; + }, + envFile(): string { + return path.join(wf.uri.fsPath, '.env'); + }, + autoTestDiscoverOnSaveEnabled(): boolean { + return true; + }, + getUnittestConfiguration(): IUnittestConfiguration { + return { + isUnittestEnabled: true, + unittestArguments: { + startDirectory: '.', + pattern: 'test_*.py', + }, + }; + }, + getPytestConfiguration(): IPytestConfiguration { + throw new Error(); + }, + getTestplanConfiguration(): ITestplanConfiguration { + throw new Error(); + }, + }, + wf, + logger() + ); } export function createTestplanConfiguration(folder: string, args?: string[], cwd?: string): IWorkspaceConfiguration { const python = getPythonExecutable(); const wf = findWorkspaceFolder(folder)!; - return new PlaceholderAwareWorkspaceConfiguration({ - pythonPath(): string { - return python; - }, - getCwd(): string { - return cwd || wf.uri.fsPath; - }, - envFile(): string { - return path.join(wf.uri.fsPath, '..', '.env'); - }, - autoTestDiscoverOnSaveEnabled(): boolean { - return true; - }, - getUnittestConfiguration(): IUnittestConfiguration { - throw new Error(); - }, - getPytestConfiguration(): IPytestConfiguration { - throw new Error(); - }, - getTestplanConfiguration(): ITestplanConfiguration { - return { - testplanPath: () => 'test_plan.py', - isTestplanEnabled: true, - testplanArguments: args || [], - }; - }, - }, wf, logger()); + return new PlaceholderAwareWorkspaceConfiguration( + { + pythonPath(): string { + return python; + }, + getCwd(): string { + return cwd || wf.uri.fsPath; + }, + envFile(): string { + return path.join(wf.uri.fsPath, '..', '.env'); + }, + autoTestDiscoverOnSaveEnabled(): boolean { + return true; + }, + getUnittestConfiguration(): IUnittestConfiguration { + throw new Error(); + }, + getPytestConfiguration(): IPytestConfiguration { + throw new Error(); + }, + getTestplanConfiguration(): ITestplanConfiguration { + return { + testplanPath: () => 'test_plan.py', + isTestplanEnabled: true, + testplanArguments: args || [], + }; + }, + }, + wf, + logger() + ); } export async function sleep(ms: number): Promise { @@ -190,40 +201,43 @@ export async function sleep(ms: number): Promise { }); } -export function extractTopLevelLablesAndDescription(suite: TestSuiteInfo): { label: string, description?: string }[] { - return suite.children.map(t => { +export function extractTopLevelLablesAndDescription(suite: TestSuiteInfo): { label: string; description?: string }[] { + return suite.children.map((t) => { return { label: t.label, description: t.description }; }); } export function expectLabelsAreSameRecursive(expected: ITreeNode, actual: TestSuiteInfo): void { const expectedLabels = Object.keys(expected); - const actualLabels = actual.children.map(t => t.label); + const actualLabels = actual.children.map((t) => t.label); expect(actualLabels).to.have.members(expectedLabels); for (const [label, expectedChild] of Object.entries(expected)) { - const actualChild = actual.children.find(t => t.label === label); + const actualChild = actual.children.find((t) => t.label === label); expect(actualChild).to.be.not.undefined; if (empty(Object.keys(expectedChild))) { expect(actualChild!.type).to.be.eq( 'test', - `Invalid node type for ${actualChild!.label} (${actualChild!.id}) ${Object.keys(expectedChild)}`); + `Invalid node type for ${actualChild!.label} (${actualChild!.id}) ${Object.keys(expectedChild)}` + ); } else { expect(actualChild!.type).to.be.eq( 'suite', - `Invalid node type for ${actualChild!.label} (${actualChild!.id}) ${Object.keys(expectedChild)}`); + `Invalid node type for ${actualChild!.label} (${actualChild!.id}) ${Object.keys(expectedChild)}` + ); expectLabelsAreSameRecursive(expectedChild, actualChild as TestSuiteInfo); } } } export function extractAllIds(suite: TestSuiteInfo): string[] { - return suite.children.map(t => { - if ((t as TestSuiteInfo).children) { - return [t.id].concat(extractAllIds(t as TestSuiteInfo)); - } - else { - return [t.id]; - } - }).reduce((r, x) => r.concat(x), []); + return suite.children + .map((t) => { + if ((t as TestSuiteInfo).children) { + return [t.id].concat(extractAllIds(t as TestSuiteInfo)); + } else { + return [t.id]; + } + }) + .reduce((r, x) => r.concat(x), []); } diff --git a/test/utils/pytest.ts b/test/utils/pytest.ts index 84161c7..d6c2356 100644 --- a/test/utils/pytest.ts +++ b/test/utils/pytest.ts @@ -1,4 +1,3 @@ - export const PYTEST_EXPECTED_SUITES_LIST_WITHOUT_ERRORS = [ 'arithmetic.py', 'describe_test.py', @@ -10,44 +9,56 @@ export const PYTEST_EXPECTED_SUITES_LIST_WITHOUT_ERRORS = [ 'subprocess_test.py', 'add_test.py', 'add_test.py', - 'test_simple.py' + 'test_simple.py', ]; export const PYTEST_EXPECTED_SUITES_LIST_WITH_ERRORS = [ { - label: 'describe_test.py', description: undefined, + label: 'describe_test.py', + description: undefined, }, { - label: 'env_variables_test.py', description: undefined, + label: 'env_variables_test.py', + description: undefined, }, { - label: 'fixture_test.py', description: undefined, + label: 'fixture_test.py', + description: undefined, }, { - label: 'generate_test.py', description: undefined, + label: 'generate_test.py', + description: undefined, }, { - label: 'inner_fixture_test.py', description: undefined, + label: 'inner_fixture_test.py', + description: undefined, }, { - label: 'string_test.py', description: undefined, + label: 'string_test.py', + description: undefined, }, { - label: 'subprocess_test.py', description: undefined, + label: 'subprocess_test.py', + description: undefined, }, { - label: 'add_test.py', description: 'inner_tests', + label: 'add_test.py', + description: 'inner_tests', }, { - label: 'add_test.py', description: 'other_tests', + label: 'add_test.py', + description: 'other_tests', }, { - label: 'test_simple.py', description: undefined, + label: 'test_simple.py', + description: undefined, }, { - label: 'Error in invalid_syntax_test.py', description: undefined, + label: 'Error in invalid_syntax_test.py', + description: undefined, }, { - label: 'Error in non_existing_module_test.py', description: undefined, - } + label: 'Error in non_existing_module_test.py', + description: undefined, + }, ]; diff --git a/test/utils/testConfiguration.ts b/test/utils/testConfiguration.ts index 959f688..7f9f6eb 100644 --- a/test/utils/testConfiguration.ts +++ b/test/utils/testConfiguration.ts @@ -1,4 +1,3 @@ - import * as path from 'path'; export function getReporter() { diff --git a/test/vscode-runner.ts b/test/vscode-runner.ts index fb043c0..8713eb8 100644 --- a/test/vscode-runner.ts +++ b/test/vscode-runner.ts @@ -1,40 +1,39 @@ - import { runTests } from 'vscode-test'; import { getPythonExecutable } from './utils/testConfiguration'; import { runScript } from '../src/pythonRunner'; import * as path from 'path'; async function main() { - try { - const python = getPythonExecutable(); - console.log(`Python executable is ${python}`); - await runScript({ - script: 'from __future__ import print_function; import sys; print(sys.executable, sys.version)', - pythonPath: python, - environment: {}, - }).complete().then(({ output }) => console.log(`Using python ${output}`)); + try { + const python = getPythonExecutable(); + console.log(`Python executable is ${python}`); + await runScript({ + script: 'from __future__ import print_function; import sys; print(sys.executable, sys.version)', + pythonPath: python, + environment: {}, + }) + .complete() + .then(({ output }) => console.log(`Using python ${output}`)); - // The folder containing the Extension Manifest package.json - // Passed to `--extensionDevelopmentPath` - const extensionDevelopmentPath = path.resolve(__dirname, '../../'); + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, '../../'); - // The path to the extension test runner script - // Passed to --extensionTestsPath - const extensionTestsPath = path.resolve(__dirname, './mocha-runner'); + // The path to the extension test runner script + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, './mocha-runner'); - // Download VS Code, unzip it and run the integration test - await runTests({ - extensionDevelopmentPath, - extensionTestsPath, - launchArgs: [ - path.resolve(extensionDevelopmentPath, 'test/test_samples/samples-workspace.code-workspace') - ], - }); - } catch (error) { - console.error(error); - console.error('Failed to run tests'); - process.exit(1); - } + // Download VS Code, unzip it and run the integration test + await runTests({ + extensionDevelopmentPath, + extensionTestsPath, + launchArgs: [path.resolve(extensionDevelopmentPath, 'test/test_samples/samples-workspace.code-workspace')], + }); + } catch (error) { + console.error(error); + console.error('Failed to run tests'); + process.exit(1); + } } main(); diff --git a/tslint.json b/tslint.json index 3fdba19..887b3e3 100644 --- a/tslint.json +++ b/tslint.json @@ -1,14 +1,12 @@ { - "extends": [ - "tslint:recommended" - ], + "extends": ["tslint:recommended", "tslint-config-prettier"], + "rulesDirectory": ["tslint-plugin-prettier"], "rules": { + "prettier": true, "curly": true, "semicolon": [true, "always"], "max-line-length": { - "options": [ - 120 - ] + "options": [120] }, "new-parens": true, "no-arg": true, @@ -17,28 +15,12 @@ "no-consecutive-blank-lines": false, "no-console": { "severity": "warning", - "options": [ - "debug", - "info", - "log", - "time", - "timeEnd", - "trace" - ] + "options": ["debug", "info", "log", "time", "timeEnd", "trace"] }, - "quotemark": [ - true, - "single", - "avoid-template" - ], + "quotemark": [true, "single", "avoid-escape", "avoid-template"], "no-angle-bracket-type-assertion": false, - "arrow-parens": [ - true, - "ban-single-arg-parens" - ], - "object-literal-sort-keys": [ - false - ], + "arrow-parens": [true], + "object-literal-sort-keys": [false], "whitespace": [ true, "check-branch", @@ -56,7 +38,7 @@ { "multiline": { "objects": "always", - "arrays": "never", + "arrays": "always", "functions": "never", "typeLiterals": "ignore" }, @@ -67,12 +49,8 @@ }, "jsRules": { "max-line-length": { - "options": [ - 120 - ] + "options": [120] }, - "object-literal-sort-keys": [ - false - ] + "object-literal-sort-keys": [false] } } From 87daf51b024c18bbad7af248f5e26a489a6e684f Mon Sep 17 00:00:00 2001 From: Krisztian Notaisz <61833595+kn-ms@users.noreply.github.com> Date: Thu, 17 Aug 2023 18:41:57 +0200 Subject: [PATCH 2/3] keep consistent line endings on windows as well --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..94f480d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file From d120daf104eb95770e759f72161e8c6582c3c47b Mon Sep 17 00:00:00 2001 From: Krisztian Notaisz <61833595+kn-ms@users.noreply.github.com> Date: Thu, 31 Aug 2023 10:42:51 +0200 Subject: [PATCH 3/3] add prettier to vscode plugin recommendation --- .vscode/extensions.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 376ae96..17eeb32 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,8 +3,9 @@ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp // List of extensions which should be recommended for users of this workspace. "recommendations": [ - "ms-vscode.vscode-typescript-tslint-plugin" + "ms-vscode.vscode-typescript-tslint-plugin", + "esbenp.prettier-vscode" ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [] -} \ No newline at end of file +}