Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions vscode/microsoft-kiota/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -520,12 +520,17 @@
"@vscode/l10n": "^0.0.18"
},
"devDependencies": {
"@stylistic/eslint-plugin-ts": "^4.4.1",
"@types/chai": "^5.0.1",
"@types/mocha": "^10.0.10",
"@types/sinon": "^17.0.4",
"@types/vscode": "^1.101.0",
"@typescript-eslint/eslint-plugin": "^8.34.1",
"@typescript-eslint/parser": "^8.34.1",
"@vscode/test-cli": "^0.0.11",
"@vscode/test-electron": "^2.4.1",
"chai": "^5.2.0",
"eslint": "^9.29.0",
"mocha": "^11.1.0",
"sinon": "^21.0.0",
"webpack": "^5.98.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ export class SearchOrOpenApiDescriptionCommand extends Command {

public async execute(searchParams: Partial<IntegrationParams>): Promise<void> {
// set deeplink params if exists
let validatedParams: Partial<IntegrationParams> = {};
if (Object.keys(searchParams).length > 0) {
let [params, errorsArray] = validateDeepLinkQueryParams(searchParams);
validatedParams = params;
setDeepLinkParams(params);
const reporter = new TelemetryReporter(this.context.extension.packageJSON.telemetryInstrumentationKey);
reporter.sendTelemetryEvent("DeepLinked searchOrOpenApiDescription", {
Expand All @@ -51,20 +53,28 @@ export class SearchOrOpenApiDescriptionCommand extends Command {
}
}

const config = await searchSteps(x => vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
cancellable: false,
title: vscode.l10n.t("Searching...")
}, async (progress, _) => {
const settings = getExtensionSettings(extensionId);
try {
return await searchDescription({ searchTerm: x, clearCache: settings.clearCache });
} catch (err) {
const error = err as Error;
vscode.window.showErrorMessage(error.message);
return undefined;
}
}));
let config: { descriptionPath?: string } = {};

// Check if descriptionurl is provided in validated params, if so skip search steps
if (validatedParams.descriptionurl && validatedParams.descriptionurl.trim() !== '') {
config.descriptionPath = validatedParams.descriptionurl;
} else {
// Only call searchSteps if no descriptionurl is provided
config = await searchSteps(x => vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
cancellable: false,
title: vscode.l10n.t("Searching...")
}, async (progress, _) => {
const settings = getExtensionSettings(extensionId);
try {
return await searchDescription({ searchTerm: x, clearCache: settings.clearCache });
} catch (err) {
const error = err as Error;
vscode.window.showErrorMessage(error.message);
return undefined;
}
}));
}

if (config.descriptionPath) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { TelemetryReporter } from "@vscode/extension-telemetry";
import assert from "assert";
import * as sinon from "sinon";
import * as vscode from 'vscode';

import { SearchOrOpenApiDescriptionCommand } from "../../../commands/openApidescription/searchOrOpenApiDescriptionCommand";
import * as deepLinkParamsHandler from "../../../handlers/deepLinkParamsHandler";
import * as searchStepsModule from "../../../modules/steps/searchSteps";
import * as openApiTreeProviderModule from "../../../providers/openApiTreeProvider";
import { IntegrationParams } from "../../../utilities/deep-linking";
import * as progressModule from "../../../utilities/progress";

let context: vscode.ExtensionContext = {
subscriptions: [],
workspaceState: {
update: sinon.stub().resolves(),
keys: sinon.stub().returns([]),
get: sinon.stub().returns(undefined)
} as vscode.Memento,
globalState: {
update: sinon.stub().resolves(),
keys: sinon.stub().returns([]),
get: sinon.stub().returns(false),
setKeysForSync: sinon.stub()
} as vscode.Memento & { setKeysForSync(keys: readonly string[]): void; },
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<any>
};

describe('SearchOrOpenApiDescriptionCommand', () => {
let openApiTreeProvider: openApiTreeProviderModule.OpenApiTreeProvider;
let command: SearchOrOpenApiDescriptionCommand;
let searchStepsStub: sinon.SinonStub;
let openProgressStub: sinon.SinonStub;
let setDescriptionUrlStub: sinon.SinonStub;
let hasChangesStub: sinon.SinonStub;

beforeEach(() => {
// Create mock OpenApiTreeProvider
openApiTreeProvider = {} as openApiTreeProviderModule.OpenApiTreeProvider;
hasChangesStub = sinon.stub().returns(false);
setDescriptionUrlStub = sinon.stub().resolves();
openApiTreeProvider.hasChanges = hasChangesStub;
openApiTreeProvider.setDescriptionUrl = setDescriptionUrlStub;

command = new SearchOrOpenApiDescriptionCommand(openApiTreeProvider, context);

// Mock the searchSteps function
searchStepsStub = sinon.stub(searchStepsModule, 'searchSteps').resolves({
descriptionPath: undefined
});

// Mock openTreeViewWithProgress
openProgressStub = sinon.stub(progressModule, 'openTreeViewWithProgress').callsFake(async (callback: () => Promise<unknown>) => {
await callback();
});

// Clean up deep link params
deepLinkParamsHandler.clearDeepLinkParams();
});

afterEach(() => {
sinon.restore();
});

it('should call searchSteps when no descriptionurl is provided', async () => {
const searchParams: Partial<IntegrationParams> = {
name: 'TestClient',
kind: 'client'
};

await command.execute(searchParams);

assert(searchStepsStub.calledOnce, 'searchSteps should be called when no descriptionurl is provided');
});

it('should skip searchSteps when descriptionurl is provided', async () => {
const searchParams: Partial<IntegrationParams> = {
descriptionurl: 'https://example.com/openapi.json',
name: 'TestClient',
kind: 'client'
};

await command.execute(searchParams);

assert(searchStepsStub.notCalled, 'searchSteps should NOT be called when descriptionurl is provided');
assert(setDescriptionUrlStub.calledOnceWith('https://example.com/openapi.json'), 'setDescriptionUrl should be called with the provided URL');
});

it('should handle local file paths in descriptionurl', async () => {
const searchParams: Partial<IntegrationParams> = {
descriptionurl: '/path/to/local/openapi.yaml',
name: 'TestClient',
kind: 'client'
};

await command.execute(searchParams);

assert(searchStepsStub.notCalled, 'searchSteps should NOT be called when descriptionurl with local path is provided');
assert(setDescriptionUrlStub.calledOnceWith('/path/to/local/openapi.yaml'), 'setDescriptionUrl should be called with the provided local path');
});

it('should call searchSteps when descriptionurl is empty', async () => {
const searchParams: Partial<IntegrationParams> = {
descriptionurl: '',
name: 'TestClient',
kind: 'client'
};

await command.execute(searchParams);

assert(searchStepsStub.calledOnce, 'searchSteps should be called when descriptionurl is empty');
});
});
Loading