Skip to content

Commit 650b09c

Browse files
Merge pull request #3444 from github/robertbrignull/start-modeling-test
Add tests of MethodModelingViewProvider.viewLoaded
2 parents b436fbd + 18f9b5f commit 650b09c

5 files changed

Lines changed: 216 additions & 14 deletions

File tree

extensions/ql-vscode/src/common/vscode/abstract-webview-view-provider.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import type {
2-
CancellationToken,
3-
WebviewView,
4-
WebviewViewProvider,
5-
WebviewViewResolveContext,
6-
} from "vscode";
1+
import type { WebviewView, WebviewViewProvider } from "vscode";
72
import { Uri } from "vscode";
83
import type { WebviewKind, WebviewMessage } from "./webview-html";
94
import { getHtmlForWebview } from "./webview-html";
@@ -28,11 +23,7 @@ export abstract class AbstractWebviewViewProvider<
2823
* This is called when a view first becomes visible. This may happen when the view is
2924
* first loaded or when the user hides and then shows a view again.
3025
*/
31-
public resolveWebviewView(
32-
webviewView: WebviewView,
33-
_context: WebviewViewResolveContext,
34-
_token: CancellationToken,
35-
) {
26+
public resolveWebviewView(webviewView: WebviewView) {
3627
webviewView.webview.options = {
3728
enableScripts: true,
3829
localResourceRoots: [Uri.file(this.app.extensionPath)],

extensions/ql-vscode/src/model-editor/modeling-store.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ interface InternalDbModelingState {
2222
modelEvaluationRun: ModelEvaluationRun | undefined;
2323
}
2424

25-
interface DbModelingState {
25+
export interface DbModelingState {
2626
readonly databaseItem: DatabaseItem;
2727
readonly methods: readonly Method[];
2828
readonly hideModeledMethods: boolean;
@@ -36,7 +36,7 @@ interface DbModelingState {
3636
readonly modelEvaluationRun: ModelEvaluationRun | undefined;
3737
}
3838

39-
interface SelectedMethodDetails {
39+
export interface SelectedMethodDetails {
4040
readonly databaseItem: DatabaseItem;
4141
readonly method: Method;
4242
readonly usage: Usage | undefined;

extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { ModelingEvents } from "../../../src/model-editor/modeling-events";
33

44
export function createMockModelingEvents({
55
onActiveDbChanged = jest.fn(),
6+
onDbOpened = jest.fn(),
67
onDbClosed = jest.fn(),
78
onSelectedMethodChanged = jest.fn(),
89
onMethodsChanged = jest.fn(),
@@ -16,6 +17,7 @@ export function createMockModelingEvents({
1617
onModelEvaluationRunChanged = jest.fn(),
1718
}: {
1819
onActiveDbChanged?: ModelingEvents["onActiveDbChanged"];
20+
onDbOpened?: ModelingEvents["onDbOpened"];
1921
onDbClosed?: ModelingEvents["onDbClosed"];
2022
onSelectedMethodChanged?: ModelingEvents["onSelectedMethodChanged"];
2123
onMethodsChanged?: ModelingEvents["onMethodsChanged"];
@@ -30,6 +32,7 @@ export function createMockModelingEvents({
3032
} = {}): ModelingEvents {
3133
return mockedObject<ModelingEvents>({
3234
onActiveDbChanged,
35+
onDbOpened,
3336
onDbClosed,
3437
onSelectedMethodChanged,
3538
onMethodsChanged,

extensions/ql-vscode/test/__mocks__/model-editor/modelingStoreMock.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@ import type { ModelingStore } from "../../../src/model-editor/modeling-store";
33

44
export function createMockModelingStore({
55
initializeStateForDb = jest.fn(),
6-
getStateForActiveDb = jest.fn(),
6+
getStateForActiveDb = jest.fn().mockReturnValue(undefined),
7+
getSelectedMethodDetails = jest.fn().mockReturnValue(undefined),
78
getModelEvaluationRun = jest.fn(),
89
updateModelEvaluationRun = jest.fn(),
910
}: {
1011
initializeStateForDb?: ModelingStore["initializeStateForDb"];
1112
getStateForActiveDb?: ModelingStore["getStateForActiveDb"];
13+
getSelectedMethodDetails?: ModelingStore["getSelectedMethodDetails"];
1214
getModelEvaluationRun?: ModelingStore["getModelEvaluationRun"];
1315
updateModelEvaluationRun?: ModelingStore["updateModelEvaluationRun"];
1416
} = {}): ModelingStore {
1517
return mockedObject<ModelingStore>({
1618
initializeStateForDb,
19+
getSelectedMethodDetails,
1720
getStateForActiveDb,
1821
getModelEvaluationRun,
1922
updateModelEvaluationRun,
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import type { Uri, Webview, WebviewView } from "vscode";
2+
import { EventEmitter } from "vscode";
3+
import type { ModelConfigListener } from "../../../../../src/config";
4+
import { MethodModelingViewProvider } from "../../../../../src/model-editor/method-modeling/method-modeling-view-provider";
5+
import { createMockApp } from "../../../../__mocks__/appMock";
6+
import { createMockModelingEvents } from "../../../../__mocks__/model-editor/modelingEventsMock";
7+
import { createMockModelingStore } from "../../../../__mocks__/model-editor/modelingStoreMock";
8+
import { mockedObject } from "../../../../mocked-object";
9+
import type { FromMethodModelingMessage } from "../../../../../src/common/interface-types";
10+
import { DisposableObject } from "../../../../../src/common/disposable-object";
11+
import type { ModelingEvents } from "../../../../../src/model-editor/modeling-events";
12+
import type {
13+
DbModelingState,
14+
ModelingStore,
15+
SelectedMethodDetails,
16+
} from "../../../../../src/model-editor/modeling-store";
17+
import { mockDatabaseItem } from "../../../utils/mocking.helpers";
18+
import {
19+
createMethod,
20+
createUsage,
21+
} from "../../../../factories/model-editor/method-factories";
22+
23+
describe("method modeling view provider", () => {
24+
// Modeling store
25+
let getStateForActiveDb: jest.MockedFunction<
26+
ModelingStore["getStateForActiveDb"]
27+
>;
28+
let getSelectedMethodDetails: jest.MockedFunction<
29+
ModelingStore["getSelectedMethodDetails"]
30+
>;
31+
32+
// Modeling events
33+
let selectedMethodChangedEventEmitter: ModelingEvents["onSelectedMethodChangedEventEmitter"];
34+
let dbOpenedEventEmitter: ModelingEvents["onDbOpenedEventEmitter"];
35+
36+
// View provider
37+
let viewProvider: MethodModelingViewProvider;
38+
let onDidReceiveMessage: (msg: FromMethodModelingMessage) => Promise<void>;
39+
let postMessage: (message: unknown) => Promise<boolean>;
40+
41+
beforeEach(async () => {
42+
const app = createMockApp({});
43+
44+
getStateForActiveDb = jest.fn().mockReturnValue(undefined);
45+
getSelectedMethodDetails = jest.fn().mockReturnValue(undefined);
46+
const modelingStore = createMockModelingStore({
47+
getStateForActiveDb,
48+
getSelectedMethodDetails,
49+
});
50+
51+
selectedMethodChangedEventEmitter = new EventEmitter();
52+
dbOpenedEventEmitter = new EventEmitter();
53+
const modelingEvents = createMockModelingEvents({
54+
onSelectedMethodChanged: selectedMethodChangedEventEmitter.event,
55+
onDbOpened: dbOpenedEventEmitter.event,
56+
});
57+
58+
const modelConfigListener = mockedObject<ModelConfigListener>({
59+
showTypeModels: true,
60+
onDidChangeConfiguration: jest.fn(),
61+
});
62+
63+
viewProvider = new MethodModelingViewProvider(
64+
app,
65+
modelingStore,
66+
modelingEvents,
67+
modelConfigListener,
68+
);
69+
70+
postMessage = jest.fn().mockResolvedValue(true);
71+
const webview: Webview = {
72+
options: {},
73+
html: "",
74+
onDidReceiveMessage: (listener) => {
75+
onDidReceiveMessage = listener;
76+
return new DisposableObject();
77+
},
78+
postMessage,
79+
asWebviewUri: (uri: Uri) => uri,
80+
cspSource: "",
81+
};
82+
83+
const webviewView = mockedObject<WebviewView>({
84+
webview,
85+
onDidDispose: jest.fn(),
86+
});
87+
88+
viewProvider.resolveWebviewView(webviewView);
89+
90+
expect(onDidReceiveMessage).toBeDefined();
91+
});
92+
93+
it("should load webview when no active DB", async () => {
94+
await onDidReceiveMessage({
95+
t: "viewLoaded",
96+
viewName: MethodModelingViewProvider.viewType,
97+
});
98+
99+
expect(postMessage).toHaveBeenCalledTimes(1);
100+
expect(postMessage).toHaveBeenCalledWith({
101+
t: "setMethodModelingPanelViewState",
102+
viewState: {
103+
language: undefined,
104+
modelConfig: {
105+
showTypeModels: true,
106+
},
107+
},
108+
});
109+
});
110+
111+
it("should load webview when active DB but no selected method", async () => {
112+
const dbModelingState = mockedObject<DbModelingState>({
113+
databaseItem: mockDatabaseItem({
114+
language: "java",
115+
}),
116+
});
117+
getStateForActiveDb.mockReturnValue(dbModelingState);
118+
119+
await onDidReceiveMessage({
120+
t: "viewLoaded",
121+
viewName: MethodModelingViewProvider.viewType,
122+
});
123+
124+
expect(postMessage).toHaveBeenCalledTimes(3);
125+
expect(postMessage).toHaveBeenNthCalledWith(1, {
126+
t: "setMethodModelingPanelViewState",
127+
viewState: {
128+
language: undefined,
129+
modelConfig: {
130+
showTypeModels: true,
131+
},
132+
},
133+
});
134+
expect(postMessage).toHaveBeenNthCalledWith(2, {
135+
t: "setInModelingMode",
136+
inModelingMode: true,
137+
});
138+
expect(postMessage).toHaveBeenNthCalledWith(3, {
139+
t: "setMethodModelingPanelViewState",
140+
viewState: {
141+
language: "java",
142+
modelConfig: {
143+
showTypeModels: true,
144+
},
145+
},
146+
});
147+
});
148+
149+
it("should load webview when active DB and a selected method", async () => {
150+
const dbModelingState = mockedObject<DbModelingState>({
151+
databaseItem: mockDatabaseItem({
152+
language: "java",
153+
}),
154+
});
155+
getStateForActiveDb.mockReturnValue(dbModelingState);
156+
157+
const selectedMethodDetails: SelectedMethodDetails = {
158+
databaseItem: dbModelingState.databaseItem,
159+
method: createMethod(),
160+
usage: createUsage(),
161+
modeledMethods: [],
162+
isModified: false,
163+
isInProgress: false,
164+
processedByAutoModel: false,
165+
};
166+
getSelectedMethodDetails.mockReturnValue(selectedMethodDetails);
167+
168+
await onDidReceiveMessage({
169+
t: "viewLoaded",
170+
viewName: MethodModelingViewProvider.viewType,
171+
});
172+
173+
expect(postMessage).toHaveBeenCalledTimes(4);
174+
expect(postMessage).toHaveBeenNthCalledWith(1, {
175+
t: "setMethodModelingPanelViewState",
176+
viewState: {
177+
language: undefined,
178+
modelConfig: {
179+
showTypeModels: true,
180+
},
181+
},
182+
});
183+
expect(postMessage).toHaveBeenNthCalledWith(2, {
184+
t: "setInModelingMode",
185+
inModelingMode: true,
186+
});
187+
expect(postMessage).toHaveBeenNthCalledWith(3, {
188+
t: "setMethodModelingPanelViewState",
189+
viewState: {
190+
language: "java",
191+
modelConfig: {
192+
showTypeModels: true,
193+
},
194+
},
195+
});
196+
expect(postMessage).toHaveBeenNthCalledWith(4, {
197+
t: "setSelectedMethod",
198+
method: selectedMethodDetails.method,
199+
modeledMethods: selectedMethodDetails.modeledMethods,
200+
isModified: selectedMethodDetails.isModified,
201+
isInProgress: selectedMethodDetails.isInProgress,
202+
processedByAutoModel: selectedMethodDetails.processedByAutoModel,
203+
});
204+
});
205+
});

0 commit comments

Comments
 (0)