diff --git a/.github/workflows/build-vscode-extension.yml b/.github/workflows/build-vscode-extension.yml index 1ba26618a9..ee28133534 100644 --- a/.github/workflows/build-vscode-extension.yml +++ b/.github/workflows/build-vscode-extension.yml @@ -31,6 +31,21 @@ jobs: working-directory: vscode/microsoft-kiota - run: npm run lint working-directory: vscode/microsoft-kiota + - name: run tests(linux) + if: runner.os == 'Linux' + run: xvfb-run -a npm run test-with-coverage + working-directory: vscode/microsoft-kiota + - name: run tests + if: runner.os != 'Linux' + run: npm run test-with-coverage + working-directory: vscode/microsoft-kiota + - name: Run sonar cloud analysis + uses: SonarSource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + with: + projectBaseDir: vscode/microsoft-kiota - run: npm run package working-directory: vscode/microsoft-kiota - run: npm i -g @vscode/vsce diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml deleted file mode 100644 index 56c1d98a16..0000000000 --- a/.github/workflows/sonarcloud.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Sonarcloud -on: - workflow_dispatch: - push: - branches: - - main - paths-ignore: - [ - "abstractions/**", - "authentication/**", - "serialization/**", - "http/**", - "**.md", - ".vscode/**", - "**.svg", - ] - pull_request: - types: [opened, synchronize, reopened] - paths-ignore: - [ - "abstractions/**", - "authentication/**", - "serialization/**", - "http/**", - "**.md", - ".vscode/**", - "**.svg", - ] - -permissions: - contents: read - pull-requests: read - -env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - -jobs: - checksecret: - name: check if SONAR_TOKEN is set in github secrets - runs-on: ubuntu-latest - outputs: - is_SONAR_TOKEN_set: ${{ steps.checksecret_job.outputs.is_SONAR_TOKEN_set }} - steps: - - name: Check whether unity activation requests should be done - id: checksecret_job - run: | - echo "is_SONAR_TOKEN_set=${{ env.SONAR_TOKEN != '' }}" >> $GITHUB_OUTPUT - build: - needs: [checksecret] - if: needs.checksecret.outputs.is_SONAR_TOKEN_set == 'true' - name: Build - runs-on: ubuntu-latest - steps: - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: "adopt" - java-version: 17 - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: # At the moment the scanner requires dotnet 5 https://www.nuget.org/packages/dotnet-sonarscanner - dotnet-version: | - 5.x - 8.x - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Cache SonarCloud packages - uses: actions/cache@v4 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar - - name: Install SonarCloud scanner - run: dotnet tool install dotnet-sonarscanner --create-manifest-if-needed - - name: Build and analyze - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any - CollectCoverage: true - CoverletOutputFormat: "opencover" # https://github.com/microsoft/vstest/issues/4014#issuecomment-1307913682 - shell: pwsh - run: | - dotnet tool run dotnet-sonarscanner begin /k:"microsoft_kiota" /o:"microsoft" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.opencover.reportsPaths="tests/**/coverage.opencover.xml" - dotnet build - dotnet test kiota.sln --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover - dotnet tool run dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" diff --git a/vscode/microsoft-kiota/.gitignore b/vscode/microsoft-kiota/.gitignore index c6a9188350..4ddd0f1fd8 100644 --- a/vscode/microsoft-kiota/.gitignore +++ b/vscode/microsoft-kiota/.gitignore @@ -3,3 +3,4 @@ out/ .kiotabin/ *.vsix .vscode-test +coverage/ diff --git a/vscode/microsoft-kiota/.vscode-test.mjs b/vscode/microsoft-kiota/.vscode-test.mjs index b62ba25f01..08a5c36613 100644 --- a/vscode/microsoft-kiota/.vscode-test.mjs +++ b/vscode/microsoft-kiota/.vscode-test.mjs @@ -1,5 +1,14 @@ import { defineConfig } from '@vscode/test-cli'; export default defineConfig({ - files: 'out/test/**/*.test.js', + tests:[ + { + files: 'out/test/**/*.test.js' + } + ], + coverage: { + includeAll: true, + exclude: ["**/src/test", "**/dist", "**/*.test.[tj]s", "**/*.ts"], + reporter: ["text-summary", "html", "json-summary", "lcov", "cobertura"], + }, }); diff --git a/vscode/microsoft-kiota/.vscode/launch.json b/vscode/microsoft-kiota/.vscode/launch.json index d4df9e96f9..899c88285e 100644 --- a/vscode/microsoft-kiota/.vscode/launch.json +++ b/vscode/microsoft-kiota/.vscode/launch.json @@ -3,33 +3,35 @@ // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 { - "version": "0.2.0", - "configurations": [ - { - "name": "Run Extension", - "type": "extensionHost", - "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}" - ], - "outFiles": [ - "${workspaceFolder}/dist/**/*.js" - ], - "preLaunchTask": "${defaultBuildTask}" - }, - { - "name": "Extension Tests", - "type": "extensionHost", - "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" - ], - "outFiles": [ - "${workspaceFolder}/out/**/*.js", - "${workspaceFolder}/dist/**/*.js" - ], - "preLaunchTask": "tasks: watch-tests" - } - ] + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "outFiles": [ + "${workspaceFolder}/dist/**/*.js" + ], + "preLaunchTask": "${defaultBuildTask}" + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--disable-extensions", + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js", + "${workspaceFolder}/dist/**/*.js" + ], + "preLaunchTask": "tasks: watch-tests" + } + ] } diff --git a/vscode/microsoft-kiota/.vscodeignore b/vscode/microsoft-kiota/.vscodeignore index 4c827dc505..0f6f2bb0a1 100644 --- a/vscode/microsoft-kiota/.vscodeignore +++ b/vscode/microsoft-kiota/.vscodeignore @@ -14,3 +14,5 @@ vsc-extension-quickstart.md .kiotabin/ .vsix images/samples/ +.vscode-test +coverage/ diff --git a/vscode/microsoft-kiota/eslint.config.mjs b/vscode/microsoft-kiota/eslint.config.mjs index 89d4173ccf..8a696b13d9 100644 --- a/vscode/microsoft-kiota/eslint.config.mjs +++ b/vscode/microsoft-kiota/eslint.config.mjs @@ -1,9 +1,9 @@ -import stylisticTs from '@stylistic/eslint-plugin-ts' +import stylisticTs from '@stylistic/eslint-plugin-ts'; import typescriptEslint from "@typescript-eslint/eslint-plugin"; import tsParser from "@typescript-eslint/parser"; export default [{ - ignores: ["**/out", "**/dist", "**/*.d.ts", ".vscode-test", "node_modules", "eslint.config.mjs", "webpack.config.cjs"], + ignores: ["**/out", "**/dist", "**/*.d.ts", ".vscode-test", "node_modules", "eslint.config.mjs", "webpack.config.cjs", "coverage"], }, { files: ["**/*.ts"], plugins: { @@ -31,4 +31,4 @@ export default [{ "no-throw-literal": "warn", semi: "off", }, -}]; \ No newline at end of file +}]; diff --git a/vscode/microsoft-kiota/package-lock.json b/vscode/microsoft-kiota/package-lock.json index 0cbce139eb..c106650bbc 100644 --- a/vscode/microsoft-kiota/package-lock.json +++ b/vscode/microsoft-kiota/package-lock.json @@ -19,7 +19,6 @@ "devDependencies": { "@stylistic/eslint-plugin-ts": "^2.9.0", "@types/adm-zip": "^0.5.5", - "@types/chai": "^5.0.0", "@types/mocha": "^10.0.9", "@types/node": "22.x", "@types/sinon": "^17.0.3", @@ -28,14 +27,12 @@ "@typescript-eslint/parser": "^8.11.0", "@vscode/test-cli": "^0.0.10", "@vscode/test-electron": "^2.4.1", - "chai": "^5.1.1", "eslint": "^9.13.0", "glob": "^11.0.0", "mocha": "^10.7.3", "sinon": "^19.0.2", "ts-loader": "^9.5.1", - "typemoq": "^2.1.0", - "typescript": "^5.5.4", + "typescript": "^5.6.3", "webpack": "^5.95.0", "webpack-cli": "^5.1.4" }, @@ -580,13 +577,6 @@ "@types/node": "*" } }, - "node_modules/@types/chai": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.0.0.tgz", - "integrity": "sha512-+DwhEHAaFPPdJ2ral3kNHFQXnTfscEEFsUxzD+d7nlcLrFK23JtNjH71RGasTcHb88b4vVi4mTyfpf8u2L8bdA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/estree": { "version": "1.0.6", "dev": true, @@ -1385,16 +1375,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "dev": true, @@ -1695,23 +1675,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/chai": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", - "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/chalk": { "version": "4.1.2", "dev": true, @@ -1727,16 +1690,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, "node_modules/chokidar": { "version": "3.5.3", "dev": true, @@ -1782,14 +1735,6 @@ "node": ">=6.0" } }, - "node_modules/circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "deprecated": "CircularJSON is in maintenance only, flatted is its successor.", - "dev": true, - "license": "MIT" - }, "node_modules/clean-stack": { "version": "4.2.0", "license": "MIT", @@ -2022,16 +1967,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/deep-is": { "version": "0.1.4", "dev": true, @@ -3170,13 +3105,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -3204,13 +3132,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loupe": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", - "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", - "dev": true, - "license": "MIT" - }, "node_modules/lowercase-keys": { "version": "3.0.0", "license": "MIT", @@ -3773,16 +3694,6 @@ "node": ">=16" } }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, "node_modules/picocolors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", @@ -3860,17 +3771,6 @@ "node": ">=8" } }, - "node_modules/postinstall-build": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postinstall-build/-/postinstall-build-5.0.3.tgz", - "integrity": "sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg==", - "deprecated": "postinstall-build's behavior is now built into npm! You should migrate off of postinstall-build and use the new `prepare` lifecycle script with npm 5.0.0 or greater.", - "dev": true, - "license": "MIT", - "bin": { - "postinstall-build": "cli.js" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "dev": true, @@ -4574,24 +4474,10 @@ "node": ">=4" } }, - "node_modules/typemoq": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/typemoq/-/typemoq-2.1.0.tgz", - "integrity": "sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "circular-json": "^0.3.1", - "lodash": "^4.17.4", - "postinstall-build": "^5.0.1" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/typescript": { - "version": "5.5.4", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/vscode/microsoft-kiota/package.json b/vscode/microsoft-kiota/package.json index 7aed327e73..7087402978 100644 --- a/vscode/microsoft-kiota/package.json +++ b/vscode/microsoft-kiota/package.json @@ -461,15 +461,15 @@ "package": "webpack --mode production --devtool hidden-source-map", "compile-tests": "tsc -p . --outDir out", "watch-tests": "tsc -p . -w --outDir out", - "pretest": "npm run compile-tests && npm run compile && npm run lint", + "pretest": "npm run compile && npm run compile-tests && npm run lint", "lint": "eslint", - "test": "vscode-test" + "test": "vscode-test", + "test-with-coverage": "npm run pretest && vscode-test --coverage" }, "devDependencies": { "@stylistic/eslint-plugin-ts": "^2.9.0", "@types/adm-zip": "^0.5.5", "@types/mocha": "^10.0.9", - "@types/chai": "^5.0.0", "@types/node": "22.x", "@types/sinon": "^17.0.3", "@types/vscode": "^1.94.0", @@ -478,13 +478,11 @@ "@vscode/test-cli": "^0.0.10", "@vscode/test-electron": "^2.4.1", "eslint": "^9.13.0", - "chai": "^5.1.1", "glob": "^11.0.0", "mocha": "^10.7.3", "sinon": "^19.0.2", "ts-loader": "^9.5.1", - "typemoq": "^2.1.0", - "typescript": "^5.5.4", + "typescript": "^5.6.3", "webpack": "^5.95.0", "webpack-cli": "^5.1.4" }, diff --git a/vscode/microsoft-kiota/sonar-project.properties b/vscode/microsoft-kiota/sonar-project.properties new file mode 100644 index 0000000000..20522ffd7d --- /dev/null +++ b/vscode/microsoft-kiota/sonar-project.properties @@ -0,0 +1,12 @@ +sonar.organization=microsoft +sonar.projectKey=microsoft_kiota +sonar.host.url=https://sonarcloud.io + + +sonar.sources=src +sonar.tests=src/test/ +sonar.test.inclusions=src/test/**/*.test.ts +sonar.exclusions=node_modules, dist +sonar.scm.exclusions.disabled=true +sonar.sourceEncoding=UTF-8 +sonar.javascript.lcov.reportPaths=coverage/lcov.info diff --git a/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts b/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts index c22ffbb7fa..e38770f94e 100644 --- a/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts +++ b/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts @@ -157,7 +157,7 @@ export class GenerateClientCommand extends Command { } } - clearDeepLinkParams(); // Clear the state after the generation + clearDeepLinkParams(); // Clear the state after successful generation } } @@ -320,4 +320,4 @@ export class GenerateClientCommand extends Command { return result; } -} \ No newline at end of file +} diff --git a/vscode/microsoft-kiota/src/test/suite/commands/filterDescriptionCommand.test.ts b/vscode/microsoft-kiota/src/test/suite/commands/filterDescriptionCommand.test.ts new file mode 100644 index 0000000000..5cfede8e7d --- /dev/null +++ b/vscode/microsoft-kiota/src/test/suite/commands/filterDescriptionCommand.test.ts @@ -0,0 +1,33 @@ +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import assert from "assert"; +import * as sinon from "sinon"; +import * as vscode from 'vscode'; +import * as filterModule from "../../../commands/openApiTreeView/filterDescriptionCommand"; +import * as filterStepsModule from "../../../modules/steps/filterSteps"; +import * as treeModule from "../../../providers/openApiTreeProvider"; + + +suite('FilterDescriptionCommand Test Suite', () => { + void vscode.window.showInformationMessage('Start FilterDescriptionCommand tests.'); + const sanbox = sinon.createSandbox(); + + teardown(async () => { + sanbox.restore(); + }); + + test('test function getName of filterDescriptionCommand', () => { + var treeProvider = sinon.createStubInstance(treeModule.OpenApiTreeProvider); + const filterDescriptionCommand = new filterModule.FilterDescriptionCommand(treeProvider); + assert.strictEqual("kiota.openApiExplorer.filterDescription", filterDescriptionCommand.getName()); + }); + + test('test function execute of filterDescriptionCommand', async () => { + const filterStepsStub = sanbox.stub(filterStepsModule, 'filterSteps'); + var treeProvider = sinon.createStubInstance(treeModule.OpenApiTreeProvider); + const filterDescriptionCommand = new filterModule.FilterDescriptionCommand(treeProvider); + await filterDescriptionCommand.execute(); + sinon.assert.calledOnce(filterStepsStub); + sinon.assert.calledWith(filterStepsStub, treeProvider.filter, sinon.match.func); + }); +}); diff --git a/vscode/microsoft-kiota/src/test/suite/commands/generateClientCommand.test.ts b/vscode/microsoft-kiota/src/test/suite/commands/generateClientCommand.test.ts new file mode 100644 index 0000000000..1751716d73 --- /dev/null +++ b/vscode/microsoft-kiota/src/test/suite/commands/generateClientCommand.test.ts @@ -0,0 +1,265 @@ +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import TelemetryReporter from "@vscode/extension-telemetry"; +import assert from "assert"; +import * as path from "path"; +import * as sinon from "sinon"; +import * as vscode from 'vscode'; +import * as generateModule from "../../../commands/generate/generateClientCommand"; +import * as languageInfoModule from "../../../commands/generate/getLanguageInformation"; +import * as deepLinkParamsHandler from "../../../handlers/deepLinkParamsHandler"; +import { KiotaLogEntry } from "../../../kiotaInterop"; +import * as generateStepsModule from "../../../modules/steps/generateSteps"; +import * as dependenciesModule from "../../../providers/dependenciesViewProvider"; +import * as treeModule from "../../../providers/openApiTreeProvider"; +import { KiotaGenerationLanguage } from "../../../types/enums"; +import * as settingsModule from "../../../types/extensionSettings"; +import { WorkspaceGenerationContext } from "../../../types/WorkspaceGenerationContext"; +import { getSanitizedString } from "../../../util"; +import { IntegrationParams, transformToGenerationConfig } from "../../../utilities/deep-linking"; +import * as msgUtilitiesModule from "../../../utilities/messaging"; + + +let context: vscode.ExtensionContext = { + subscriptions: [], + workspaceState: { + update: sinon.stub().resolves(), + keys: sinon.stub().returns([]), + get: sinon.stub().returns(undefined) + } as vscode.Memento, + globalState: {} as any, + secrets: {} as vscode.SecretStorage, + extensionUri: vscode.Uri.parse(''), + extensionPath: '', + environmentVariableCollection: {} as vscode.GlobalEnvironmentVariableCollection, + storagePath: '', + globalStoragePath: '', + logPath: '', + languageModelAccessInformation: {} as any, + extensionMode: vscode.ExtensionMode.Test, + asAbsolutePath: (relativePath: string) => relativePath, + storageUri: vscode.Uri.parse(''), + globalStorageUri: vscode.Uri.parse(''), + logUri: vscode.Uri.parse(''), + extension: { + packageJSON: { + telemetryInstrumentationKey: "" + } + } as vscode.Extension +}; + +let extensionSettings = { + includeAdditionalData:false, + backingStore: false, + excludeBackwardCompatible: false, + cleanOutput: false, + clearCache: true, + disableValidationRules: [], + structuredMimeTypes: [], + languagesSerializationConfiguration: { + [KiotaGenerationLanguage.CLI]: { serializers: [], deserializers: [] }, + [KiotaGenerationLanguage.CSharp]: { serializers: [], deserializers: [] }, + [KiotaGenerationLanguage.Go]: { serializers: [], deserializers: [] }, + [KiotaGenerationLanguage.Java]: { serializers: [], deserializers: [] }, + [KiotaGenerationLanguage.PHP]: { serializers: [], deserializers: [] }, + [KiotaGenerationLanguage.Python]: { serializers: [], deserializers: [] }, + [KiotaGenerationLanguage.Ruby]: { serializers: [], deserializers: [] }, + [KiotaGenerationLanguage.Swift]: { serializers: [], deserializers: [] }, + [KiotaGenerationLanguage.TypeScript]: { serializers: [], deserializers: [] } + }, +}; + +let result: KiotaLogEntry[] = [{level: 1, message: "Parsing OpenAPI file"},{level:2, message: "Generation completed successfully"}]; + +const setWorkspaceGenerationContext = (params: Partial):void =>{}; + +suite('GenerateClientCommand Test Suite', () => { + const sanbox = sinon.createSandbox(); + + teardown(() => { + sanbox.restore(); + }); + + test('test function getName of GenerateClientCommand', () => { + var treeProvider = sinon.createStubInstance(treeModule.OpenApiTreeProvider); + var viewProvider = sinon.createStubInstance(dependenciesModule.DependenciesViewProvider); + const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext); + assert.strictEqual("kiota.openApiExplorer.generateClient", generateClientCommand.getName()); + }); + + test('test function execute of GenerateClientCommand with 0 selected paths', async () => { + var treeProvider = sinon.createStubInstance(treeModule.OpenApiTreeProvider); + treeProvider.getSelectedPaths.returns([]); + var viewProvider = sinon.createStubInstance(dependenciesModule.DependenciesViewProvider); + const vscodeWindowSpy = sinon.stub(vscode.window, "showErrorMessage"); + const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext); + await generateClientCommand.execute(); + assert.strictEqual((treeProvider.getSelectedPaths()).length, 0); + sinon.assert.calledOnceWithMatch(vscodeWindowSpy, vscode.l10n.t("No endpoints selected, select endpoints first")); + vscodeWindowSpy.restore(); + }); + + test('test function execute of GenerateClientCommand with descriptionUrl unset', async () => { + var treeProvider = sinon.createStubInstance(treeModule.OpenApiTreeProvider); + treeProvider.getSelectedPaths.returns(["repairs"]); + treeProvider.apiTitle = "Repairs OAD"; + var viewProvider = sinon.createStubInstance(dependenciesModule.DependenciesViewProvider); + const vscodeWindowSpy = sinon.mock(vscode.window).expects( + "showErrorMessage").once().withArgs( + vscode.l10n.t("No description found, select a description first") + ); + const getlanguageInfoFn = sinon.stub(languageInfoModule, "getLanguageInformation"); + getlanguageInfoFn.resolves(undefined); + let config: Partial = {generationType: "client"}; + const generateStepsFn = sinon.stub(generateStepsModule, "generateSteps"); + generateStepsFn.resolves(config); + const showUpgradeWarningMessageStub = sinon.stub(msgUtilitiesModule, "showUpgradeWarningMessage"); + const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext); + await generateClientCommand.execute(); + assert.strictEqual((treeProvider.getSelectedPaths()).length, 1); + vscodeWindowSpy.verify(); + sinon.assert.calledOnceWithMatch(getlanguageInfoFn, context); + let stateInfo: Partial = { + clientClassName: treeProvider.clientClassName, + clientNamespaceName: treeProvider.clientNamespaceName, + language: treeProvider.language, + outputPath: treeProvider.outputPath, + pluginName:"RepairsOAD" + }; + assert.strictEqual(!treeProvider.descriptionUrl, true); + sinon.assert.calledOnceWithMatch(generateStepsFn, stateInfo, undefined , {}); + sinon.assert.calledOnce(showUpgradeWarningMessageStub); + sinon.restore(); + }); + + test('test successful completion of function execute of GenerateClientCommand', async () => { + var treeProvider = sinon.createStubInstance(treeModule.OpenApiTreeProvider); + sinon.stub( + treeProvider, "descriptionUrl" + ).get( + function getterFn() { + return "https://graph.microsoft.com/v1.0/$metadata"; + } + ); + treeProvider.getSelectedPaths.returns(["repairs"]); + treeProvider.apiTitle = "Repairs OAD"; + var viewProvider = sinon.createStubInstance(dependenciesModule.DependenciesViewProvider); + const vscodeWindowSpy = sinon.mock(vscode.window).expects("showErrorMessage").never(); + const getlanguageInfoFn = sinon.stub(languageInfoModule, "getLanguageInformation"); + getlanguageInfoFn.resolves(undefined); + const showUpgradeWarningMessageStub = sinon.stub(msgUtilitiesModule, "showUpgradeWarningMessage"); + const getExtensionSettingsStub = sinon.stub(settingsModule, "getExtensionSettings").onFirstCall().returns(extensionSettings); + //set deeplinkparams with name provided. + var pluginParams: any = { + name: "OverridingRepairsOADname", //sanitized before setDeepLinkParams is called + kind: "plugin", + type: "ApiPlugin", + source: "tafutaAPI" + }; + let config: Partial = {generationType: "plugin", outputPath: "path/to/temp/folder", pluginName: pluginParams.name}; + const generateStepsFn = sinon.stub(generateStepsModule, "generateSteps"); + generateStepsFn.resolves(config); + deepLinkParamsHandler.setDeepLinkParams(pluginParams); + + //stub and call generateCommand + const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext); + const generatePluginAndRefreshUIExpectation = sinon.mock(generateClientCommand).expects( + "generatePluginAndRefreshUI").once().withArgs( + config, extensionSettings, "path/to/temp/folder", ["repairs"] + ); + generatePluginAndRefreshUIExpectation.resolves(result); + await generateClientCommand.execute(); + assert.strictEqual((treeProvider.getSelectedPaths()).length, 1); + assert.strictEqual(!treeProvider.descriptionUrl, false); + vscodeWindowSpy.verify(); + sinon.assert.calledOnceWithMatch(getlanguageInfoFn, context); + let stateInfo = transformToGenerationConfig(pluginParams); + sinon.assert.calledOnceWithMatch(generateStepsFn, stateInfo, undefined , pluginParams); + sinon.assert.calledOnce(showUpgradeWarningMessageStub); + sinon.assert.calledOnceWithMatch(getExtensionSettingsStub, "kiota"); + + // assert successful call to method generatePluginAndRefreshUI + generatePluginAndRefreshUIExpectation.verify(); + sinon.restore(); + }); + + test('test ttk integration in function execute of GenerateClientCommand', async () => { + var treeProvider = sinon.createStubInstance(treeModule.OpenApiTreeProvider); + sinon.stub( + treeProvider, "descriptionUrl" + ).get( + function getterFn() { + return "https://graph.microsoft.com/v1.0/$metadata"; + } + ); + treeProvider.getSelectedPaths.returns(["repairs"]); + treeProvider.apiTitle = "Repairs OAD"; + let sanitizedApiTitle = getSanitizedString(treeProvider.apiTitle); + var viewProvider = sinon.createStubInstance(dependenciesModule.DependenciesViewProvider); + const vscodeWindowSpy = sinon.mock(vscode.window).expects("showErrorMessage").never(); + const getlanguageInfoFn = sinon.stub(languageInfoModule, "getLanguageInformation"); + getlanguageInfoFn.resolves(undefined); + sinon.stub(msgUtilitiesModule, "showUpgradeWarningMessage"); + const clearDeepLinkParamSpy = sinon.spy(deepLinkParamsHandler, "clearDeepLinkParams"); + sinon.stub(settingsModule, "getExtensionSettings").returns(extensionSettings); + var pluginParams: any = { + kind: "plugin", + type: "apimanifest", + source: "TTK", + ttkContext: { + lastCommand: 'createDeclarativeCopilotWithManifest' + } + }; + let config: Partial = {generationType: "apimanifest", outputPath: "path/to/temp/folder", pluginName: sanitizedApiTitle}; + const generateStepsFn = sinon.stub(generateStepsModule, "generateSteps"); + generateStepsFn.resolves(config); + deepLinkParamsHandler.setDeepLinkParams(pluginParams); + + //stub and call generateCommand + const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext); + var outputPath = "path/to/temp/folder"; + const generateManifestAndRefreshUIExpectation = sinon.mock(generateClientCommand).expects( + "generateManifestAndRefreshUI").twice().withArgs( + config, extensionSettings, outputPath, ["repairs"] + ); + generateManifestAndRefreshUIExpectation.resolves(result); + let executeCommandStub = sinon.stub(vscode.commands, "executeCommand"); + executeCommandStub.resolves(); + await generateClientCommand.execute(); + + // assertions + vscodeWindowSpy.verify(); + sinon.assert.calledOnceWithMatch(getlanguageInfoFn, context); + var updatedDeepLinkParams: Partial = JSON.parse(JSON.stringify(pluginParams)); + updatedDeepLinkParams["name"] = sanitizedApiTitle; + sinon.assert.calledOnce(generateStepsFn); + sinon.assert.calledWithMatch( + executeCommandStub, + 'fx-extension.createprojectfromkiota', + [ + path.join(outputPath, `${sanitizedApiTitle?.toLowerCase()}-openapi.yml`), + path.join(outputPath, `${sanitizedApiTitle?.toLowerCase()}-apiplugin.json`), + {lastCommand: 'createDeclarativeCopilotWithManifest'} + ] + ); + sinon.assert.calledOnce(clearDeepLinkParamSpy); + + //test call to ttk createprojectfromkiota fails with undefined ttkContext Param + pluginParams = { + kind: "plugin", + type: "apimanifest", + source: "TTK", + }; + deepLinkParamsHandler.setDeepLinkParams(pluginParams); + executeCommandStub.throws("ttk context not provided"); + const telemetryStub = sinon.stub(TelemetryReporter.prototype, "sendTelemetryEvent").resolves(); + //call execute command again but this time expect call to fail + await generateClientCommand.execute(); + sinon.assert.calledWith( + telemetryStub, + "DeepLinked fx-extension.createprojectfromkiota", + {"error": '{"name":"ttk context not provided"}' } + ); + generateManifestAndRefreshUIExpectation.verify(); + }); +}); diff --git a/vscode/microsoft-kiota/src/test/suite/extension.test.ts b/vscode/microsoft-kiota/src/test/suite/extension.test.ts deleted file mode 100644 index 294ee539cf..0000000000 --- a/vscode/microsoft-kiota/src/test/suite/extension.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -// You can import and use all API from the 'vscode' module -// as well as import your extension to test it -// import { assert } from "chai"; -import assert from "assert"; -import * as sinon from "sinon"; -import * as vscode from 'vscode'; -import * as filterModule from "../../commands/open-api-tree-view/filterDescriptionCommand"; -import * as treeModule from "../../openApiTreeProvider"; - - -suite('Extension Test Suite', () => { - void vscode.window.showInformationMessage('Start all tests.'); - const sanbox = sinon.createSandbox(); - - teardown(async () => { - sanbox.restore(); - }); - - test('Sample test', () => { - assert.strictEqual(-1, [1, 2, 3].indexOf(5)); - assert.strictEqual(-1, [1, 2, 3].indexOf(0)); - }); - - test('test open-api-tree-view function filterDescriptionCommand', () => { - var treeProvider = sinon.createStubInstance(treeModule.OpenApiTreeProvider); - const filterDescriptionCommand = new filterModule.FilterDescriptionCommand(treeProvider); - assert.strictEqual("kiota.openApiExplorer.filterDescription", filterDescriptionCommand.getName()); - }); -}); diff --git a/vscode/microsoft-kiota/webpack.config.cjs b/vscode/microsoft-kiota/webpack.config.cjs index 06047c67eb..daa57fc528 100644 --- a/vscode/microsoft-kiota/webpack.config.cjs +++ b/vscode/microsoft-kiota/webpack.config.cjs @@ -51,4 +51,4 @@ const extensionConfig = { level: "log", // enables logging required for problem matchers }, }; -module.exports = [ extensionConfig ]; \ No newline at end of file +module.exports = [ extensionConfig ];