Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
Usage,
ExternalApiUsage,
CallClassification,
} from "../../../src/data-extensions-editor/external-api-usage";
import { ModeledMethodType } from "../../../src/data-extensions-editor/modeled-method";
import { ResolvableLocationValue } from "../../../src/common/bqrs-cli-types";

export function createExternalApiUsage({
library = "test",
supported = true,
supportedType = "none" as ModeledMethodType,
usages = [],
signature = "test",
packageName = "test",
typeName = "test",
methodName = "test",
methodParameters = "test",
}: {
library?: string;
supported?: boolean;
supportedType?: ModeledMethodType;
usages?: Usage[];
signature?: string;
packageName?: string;
typeName?: string;
methodName?: string;
methodParameters?: string;
} = {}): ExternalApiUsage {
return {
library,
supported,
supportedType,
usages,
signature,
packageName,
typeName,
methodName,
methodParameters,
};
}

export function createUsage({
classification = CallClassification.Unknown,
label = "test",
url = {} as ResolvableLocationValue,
}: {
classification?: CallClassification;
label?: string;
url?: ResolvableLocationValue;
} = {}): Usage {
return {
classification,
label,
url,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,28 @@ import { CodeQLCliServer } from "../../../../../src/codeql-cli/cli";
import { ExternalApiUsage } from "../../../../../src/data-extensions-editor/external-api-usage";
import { ModelDetailsDataProvider } from "../../../../../src/data-extensions-editor/model-details/model-details-data-provider";
import { DatabaseItem } from "../../../../../src/databases/local-databases";
import {
createExternalApiUsage,
createUsage,
} from "../../../../factories/data-extension/external-api-factories";
import { mockedObject } from "../../../utils/mocking.helpers";

describe("ModelDetailsDataProvider", () => {
const mockCliServer = mockedObject<CodeQLCliServer>({});
let dataProvider: ModelDetailsDataProvider;

beforeEach(() => {
dataProvider = new ModelDetailsDataProvider(mockCliServer);
});

describe("setState", () => {
const hideModeledApis: boolean = false;
const hideModeledApis = false;
const externalApiUsages: ExternalApiUsage[] = [];
const dbItem = mockedObject<DatabaseItem>({
getSourceLocationPrefix: () => "test",
});

it("should not emit onDidChangeTreeData event when state has not changed", async () => {
const dataProvider = new ModelDetailsDataProvider(mockCliServer);
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis);

const onDidChangeTreeDataListener = jest.fn();
Expand All @@ -29,7 +37,6 @@ describe("ModelDetailsDataProvider", () => {
it("should emit onDidChangeTreeData event when externalApiUsages has changed", async () => {
const externalApiUsages2: ExternalApiUsage[] = [];

const dataProvider = new ModelDetailsDataProvider(mockCliServer);
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis);

const onDidChangeTreeDataListener = jest.fn();
Expand All @@ -45,7 +52,6 @@ describe("ModelDetailsDataProvider", () => {
getSourceLocationPrefix: () => "test",
});

const dataProvider = new ModelDetailsDataProvider(mockCliServer);
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis);

const onDidChangeTreeDataListener = jest.fn();
Expand All @@ -57,7 +63,6 @@ describe("ModelDetailsDataProvider", () => {
});

it("should emit onDidChangeTreeData event when hideModeledApis has changed", async () => {
const dataProvider = new ModelDetailsDataProvider(mockCliServer);
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis);

const onDidChangeTreeDataListener = jest.fn();
Expand All @@ -74,7 +79,6 @@ describe("ModelDetailsDataProvider", () => {
});
const externalApiUsages2: ExternalApiUsage[] = [];

const dataProvider = new ModelDetailsDataProvider(mockCliServer);
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis);

const onDidChangeTreeDataListener = jest.fn();
Expand All @@ -89,4 +93,45 @@ describe("ModelDetailsDataProvider", () => {
expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1);
});
});

describe("getChildren", () => {
const supportedExternalApiUsage = createExternalApiUsage({
supported: true,
});

const unsupportedExternalApiUsage = createExternalApiUsage({
supported: false,
});

const externalApiUsages: ExternalApiUsage[] = [
supportedExternalApiUsage,
unsupportedExternalApiUsage,
];
const dbItem = mockedObject<DatabaseItem>({
getSourceLocationPrefix: () => "test",
});

const usage = createUsage({});

it("should return [] if item is a usage", async () => {
expect(dataProvider.getChildren(usage)).toEqual([]);
});

it("should return usages if item is external api usage", async () => {
const externalApiUsage = createExternalApiUsage({ usages: [usage] });
expect(dataProvider.getChildren(externalApiUsage)).toEqual([usage]);
});

it("should show all externalApiUsages if hideModeledApis is false and looking at the root", async () => {
const hideModeledApis = false;
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis);
expect(dataProvider.getChildren().length).toEqual(2);
});

it("should filter externalApiUsages if hideModeledApis is true and looking at the root", async () => {
const hideModeledApis = true;
await dataProvider.setState(externalApiUsages, dbItem, hideModeledApis);
expect(dataProvider.getChildren().length).toEqual(1);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { window, TreeView } from "vscode";
import { CodeQLCliServer } from "../../../../../src/codeql-cli/cli";
import { ExternalApiUsage } from "../../../../../src/data-extensions-editor/external-api-usage";
import { ModelDetailsPanel } from "../../../../../src/data-extensions-editor/model-details/model-details-panel";
import { DatabaseItem } from "../../../../../src/databases/local-databases";
import { mockedObject } from "../../../utils/mocking.helpers";
import {
createExternalApiUsage,
createUsage,
} from "../../../../factories/data-extension/external-api-factories";

describe("ModelDetailsPanel", () => {
const mockCliServer = mockedObject<CodeQLCliServer>({});
const dbItem = mockedObject<DatabaseItem>({
getSourceLocationPrefix: () => "test",
});

describe("setState", () => {
const hideModeledApis = false;
const externalApiUsages: ExternalApiUsage[] = [createExternalApiUsage()];

it("should update the tree view with the correct batch number", async () => {
const mockTreeView = {
badge: undefined,
} as TreeView<unknown>;
jest.spyOn(window, "createTreeView").mockReturnValue(mockTreeView);

const panel = new ModelDetailsPanel(mockCliServer);
await panel.setState(externalApiUsages, dbItem, hideModeledApis);

expect(mockTreeView.badge?.value).toBe(1);
});
});

describe("revealItem", () => {
let mockTreeView: TreeView<unknown>;

const hideModeledApis: boolean = false;
const usage = createUsage();

beforeEach(() => {
mockTreeView = {
reveal: jest.fn(),
} as unknown as TreeView<unknown>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is similar to as any as X so we should ideally avoid it. Is there a way to get around it easily, or is creating mock TreeView<>T> items too painful? I'm happy to take it on as tech debt too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem lies in the reveal: jest.fn(). Without it we could remove the as unknown. With it we would have to mock the vs code reveal method better. I'm not sure if that is easily possible? We only check if reveal was called, not what it does. Therefore I leaned towards leaving it as is. However, can we introduce other errors by using as unknown here apart from the reveal method?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets leave it as is. I imagine creating the mock properly isn't trivial.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for talking this through. I think mockedObject could be a good solution for this case. I've opened #2768 for this where I've tried to explain my reasoning, so let me know what you think.

jest.spyOn(window, "createTreeView").mockReturnValue(mockTreeView);
});

it("should reveal the correct item in the tree view", async () => {
const externalApiUsages = [
createExternalApiUsage({
usages: [usage],
}),
];

const panel = new ModelDetailsPanel(mockCliServer);
await panel.setState(externalApiUsages, dbItem, hideModeledApis);

await panel.revealItem(usage);

expect(mockTreeView.reveal).toHaveBeenCalledWith(usage);
});

it("should do nothing if usage cannot be found", async () => {
const externalApiUsages = [createExternalApiUsage({})];
const panel = new ModelDetailsPanel(mockCliServer);
await panel.setState(externalApiUsages, dbItem, hideModeledApis);

await panel.revealItem(usage);

expect(mockTreeView.reveal).not.toHaveBeenCalled();
});
});
});