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
@@ -1,9 +1,4 @@
import type {
CancellationToken,
WebviewView,
WebviewViewProvider,
WebviewViewResolveContext,
} from "vscode";
import type { WebviewView, WebviewViewProvider } from "vscode";
import { Uri } from "vscode";
import type { WebviewKind, WebviewMessage } from "./webview-html";
import { getHtmlForWebview } from "./webview-html";
Expand All @@ -28,11 +23,7 @@ export abstract class AbstractWebviewViewProvider<
* This is called when a view first becomes visible. This may happen when the view is
* first loaded or when the user hides and then shows a view again.
*/
public resolveWebviewView(
webviewView: WebviewView,
_context: WebviewViewResolveContext,
_token: CancellationToken,
) {
public resolveWebviewView(webviewView: WebviewView) {
webviewView.webview.options = {
enableScripts: true,
localResourceRoots: [Uri.file(this.app.extensionPath)],
Expand Down
4 changes: 2 additions & 2 deletions extensions/ql-vscode/src/model-editor/modeling-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface InternalDbModelingState {
modelEvaluationRun: ModelEvaluationRun | undefined;
}

interface DbModelingState {
export interface DbModelingState {
readonly databaseItem: DatabaseItem;
readonly methods: readonly Method[];
readonly hideModeledMethods: boolean;
Expand All @@ -36,7 +36,7 @@ interface DbModelingState {
readonly modelEvaluationRun: ModelEvaluationRun | undefined;
}

interface SelectedMethodDetails {
export interface SelectedMethodDetails {
readonly databaseItem: DatabaseItem;
readonly method: Method;
readonly usage: Usage | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { ModelingEvents } from "../../../src/model-editor/modeling-events";

export function createMockModelingEvents({
onActiveDbChanged = jest.fn(),
onDbOpened = jest.fn(),
onDbClosed = jest.fn(),
onSelectedMethodChanged = jest.fn(),
onMethodsChanged = jest.fn(),
Expand All @@ -16,6 +17,7 @@ export function createMockModelingEvents({
onModelEvaluationRunChanged = jest.fn(),
}: {
onActiveDbChanged?: ModelingEvents["onActiveDbChanged"];
onDbOpened?: ModelingEvents["onDbOpened"];
onDbClosed?: ModelingEvents["onDbClosed"];
onSelectedMethodChanged?: ModelingEvents["onSelectedMethodChanged"];
onMethodsChanged?: ModelingEvents["onMethodsChanged"];
Expand All @@ -30,6 +32,7 @@ export function createMockModelingEvents({
} = {}): ModelingEvents {
return mockedObject<ModelingEvents>({
onActiveDbChanged,
onDbOpened,
onDbClosed,
onSelectedMethodChanged,
onMethodsChanged,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ import type { ModelingStore } from "../../../src/model-editor/modeling-store";

export function createMockModelingStore({
initializeStateForDb = jest.fn(),
getStateForActiveDb = jest.fn(),
getStateForActiveDb = jest.fn().mockReturnValue(undefined),
getSelectedMethodDetails = jest.fn().mockReturnValue(undefined),
getModelEvaluationRun = jest.fn(),
updateModelEvaluationRun = jest.fn(),
}: {
initializeStateForDb?: ModelingStore["initializeStateForDb"];
getStateForActiveDb?: ModelingStore["getStateForActiveDb"];
getSelectedMethodDetails?: ModelingStore["getSelectedMethodDetails"];
getModelEvaluationRun?: ModelingStore["getModelEvaluationRun"];
updateModelEvaluationRun?: ModelingStore["updateModelEvaluationRun"];
} = {}): ModelingStore {
return mockedObject<ModelingStore>({
initializeStateForDb,
getSelectedMethodDetails,
getStateForActiveDb,
getModelEvaluationRun,
updateModelEvaluationRun,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import type { Uri, Webview, WebviewView } from "vscode";
import { EventEmitter } from "vscode";
import type { ModelConfigListener } from "../../../../../src/config";
import { MethodModelingViewProvider } from "../../../../../src/model-editor/method-modeling/method-modeling-view-provider";
import { createMockApp } from "../../../../__mocks__/appMock";
import { createMockModelingEvents } from "../../../../__mocks__/model-editor/modelingEventsMock";
import { createMockModelingStore } from "../../../../__mocks__/model-editor/modelingStoreMock";
import { mockedObject } from "../../../../mocked-object";
import type { FromMethodModelingMessage } from "../../../../../src/common/interface-types";
import { DisposableObject } from "../../../../../src/common/disposable-object";
import type { ModelingEvents } from "../../../../../src/model-editor/modeling-events";
import type {
DbModelingState,
ModelingStore,
SelectedMethodDetails,
} from "../../../../../src/model-editor/modeling-store";
import { mockDatabaseItem } from "../../../utils/mocking.helpers";
import {
createMethod,
createUsage,
} from "../../../../factories/model-editor/method-factories";

describe("method modeling view provider", () => {
// Modeling store
let getStateForActiveDb: jest.MockedFunction<
ModelingStore["getStateForActiveDb"]
>;
let getSelectedMethodDetails: jest.MockedFunction<
ModelingStore["getSelectedMethodDetails"]
>;

// Modeling events
let selectedMethodChangedEventEmitter: ModelingEvents["onSelectedMethodChangedEventEmitter"];
let dbOpenedEventEmitter: ModelingEvents["onDbOpenedEventEmitter"];

// View provider
let viewProvider: MethodModelingViewProvider;
let onDidReceiveMessage: (msg: FromMethodModelingMessage) => Promise<void>;
let postMessage: (message: unknown) => Promise<boolean>;

beforeEach(async () => {
const app = createMockApp({});

getStateForActiveDb = jest.fn().mockReturnValue(undefined);
getSelectedMethodDetails = jest.fn().mockReturnValue(undefined);
const modelingStore = createMockModelingStore({
getStateForActiveDb,
getSelectedMethodDetails,
});

selectedMethodChangedEventEmitter = new EventEmitter();
dbOpenedEventEmitter = new EventEmitter();
const modelingEvents = createMockModelingEvents({
onSelectedMethodChanged: selectedMethodChangedEventEmitter.event,
onDbOpened: dbOpenedEventEmitter.event,
});

const modelConfigListener = mockedObject<ModelConfigListener>({
showTypeModels: true,
onDidChangeConfiguration: jest.fn(),
});

viewProvider = new MethodModelingViewProvider(
app,
modelingStore,
modelingEvents,
modelConfigListener,
);

postMessage = jest.fn().mockResolvedValue(true);
const webview: Webview = {
options: {},
html: "",
onDidReceiveMessage: (listener) => {
onDidReceiveMessage = listener;
return new DisposableObject();
},
postMessage,
asWebviewUri: (uri: Uri) => uri,
cspSource: "",
};

const webviewView = mockedObject<WebviewView>({
webview,
onDidDispose: jest.fn(),
});

viewProvider.resolveWebviewView(webviewView);

expect(onDidReceiveMessage).toBeDefined();
});

it("should load webview when no active DB", async () => {
await onDidReceiveMessage({
t: "viewLoaded",
viewName: MethodModelingViewProvider.viewType,
});

expect(postMessage).toHaveBeenCalledTimes(1);
expect(postMessage).toHaveBeenCalledWith({
t: "setMethodModelingPanelViewState",
viewState: {
language: undefined,
modelConfig: {
showTypeModels: true,
},
},
});
});

it("should load webview when active DB but no selected method", async () => {
const dbModelingState = mockedObject<DbModelingState>({
databaseItem: mockDatabaseItem({
language: "java",
}),
});
getStateForActiveDb.mockReturnValue(dbModelingState);

await onDidReceiveMessage({
t: "viewLoaded",
viewName: MethodModelingViewProvider.viewType,
});

expect(postMessage).toHaveBeenCalledTimes(3);
expect(postMessage).toHaveBeenNthCalledWith(1, {
t: "setMethodModelingPanelViewState",
viewState: {
language: undefined,
modelConfig: {
showTypeModels: true,
},
},
});
expect(postMessage).toHaveBeenNthCalledWith(2, {
t: "setInModelingMode",
inModelingMode: true,
});
expect(postMessage).toHaveBeenNthCalledWith(3, {
t: "setMethodModelingPanelViewState",
viewState: {
language: "java",
modelConfig: {
showTypeModels: true,
},
},
});
});

it("should load webview when active DB and a selected method", async () => {
const dbModelingState = mockedObject<DbModelingState>({
databaseItem: mockDatabaseItem({
language: "java",
}),
});
getStateForActiveDb.mockReturnValue(dbModelingState);

const selectedMethodDetails: SelectedMethodDetails = {
databaseItem: dbModelingState.databaseItem,
method: createMethod(),
usage: createUsage(),
modeledMethods: [],
isModified: false,
isInProgress: false,
processedByAutoModel: false,
};
getSelectedMethodDetails.mockReturnValue(selectedMethodDetails);

await onDidReceiveMessage({
t: "viewLoaded",
viewName: MethodModelingViewProvider.viewType,
});

expect(postMessage).toHaveBeenCalledTimes(4);
expect(postMessage).toHaveBeenNthCalledWith(1, {
t: "setMethodModelingPanelViewState",
viewState: {
language: undefined,
modelConfig: {
showTypeModels: true,
},
},
});
expect(postMessage).toHaveBeenNthCalledWith(2, {
t: "setInModelingMode",
inModelingMode: true,
});
expect(postMessage).toHaveBeenNthCalledWith(3, {
t: "setMethodModelingPanelViewState",
viewState: {
language: "java",
modelConfig: {
showTypeModels: true,
},
},
});
expect(postMessage).toHaveBeenNthCalledWith(4, {
t: "setSelectedMethod",
method: selectedMethodDetails.method,
modeledMethods: selectedMethodDetails.modeledMethods,
isModified: selectedMethodDetails.isModified,
isInProgress: selectedMethodDetails.isInProgress,
processedByAutoModel: selectedMethodDetails.processedByAutoModel,
});
});
});