Skip to content

Commit f3eefc9

Browse files
committed
Add tests for prompting for database download
1 parent 15a8655 commit f3eefc9

2 files changed

Lines changed: 166 additions & 32 deletions

File tree

extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export const QUERY_LANGUAGE_TO_DATABASE_REPO: QueryLanguagesToDatabaseMap = {
4545
export class SkeletonQueryWizard {
4646
private fileName = "example.ql";
4747
private qlPackStoragePath: string | undefined;
48+
private downloadPromise: Promise<void> | undefined;
4849

4950
constructor(
5051
private readonly cliServer: CodeQLCliServer,
@@ -60,6 +61,16 @@ export class SkeletonQueryWizard {
6061
return `codeql-custom-queries-${this.language}`;
6162
}
6263

64+
/**
65+
* Wait for the download process to complete by waiting for the user to select
66+
* either "Download database" or closing the dialog. This is used for testing.
67+
*/
68+
public async waitForDownload() {
69+
if (this.downloadPromise) {
70+
await this.downloadPromise;
71+
}
72+
}
73+
6374
public async execute() {
6475
if (!this.language) {
6576
// show quick pick to choose language
@@ -313,7 +324,9 @@ export class SkeletonQueryWizard {
313324
}
314325
} else {
315326
// download new database and select it
316-
void this.promptDownloadDatabase();
327+
this.downloadPromise = this.promptDownloadDatabase().finally(() => {
328+
this.downloadPromise = undefined;
329+
});
317330
}
318331
}
319332

extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts

Lines changed: 152 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import {
55
} from "../../../../src/local-queries/skeleton-query-wizard";
66
import { mockedObject, mockedQuickPickItem } from "../../utils/mocking.helpers";
77
import * as tmp from "tmp";
8-
import { TextDocument, window, workspace, WorkspaceFolder } from "vscode";
8+
import {
9+
MessageItem,
10+
TextDocument,
11+
window,
12+
workspace,
13+
WorkspaceFolder,
14+
} from "vscode";
915
import { extLogger } from "../../../../src/common/logging/vscode";
1016
import { QlPackGenerator } from "../../../../src/local-queries/qlpack-generator";
1117
import * as workspaceFolders from "../../../../src/common/vscode/workspace-folders";
@@ -31,6 +37,9 @@ describe("SkeletonQueryWizard", () => {
3137
let storagePath: string;
3238
let quickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
3339
let showInputBoxSpy: jest.SpiedFunction<typeof window.showInputBox>;
40+
let showInformationMessageSpy: jest.SpiedFunction<
41+
typeof window.showInformationMessage
42+
>;
3443
let generateSpy: jest.SpiedFunction<
3544
typeof QlPackGenerator.prototype.generate
3645
>;
@@ -97,6 +106,9 @@ describe("SkeletonQueryWizard", () => {
97106
showInputBoxSpy = jest
98107
.spyOn(window, "showInputBox")
99108
.mockResolvedValue(storagePath);
109+
showInformationMessageSpy = jest
110+
.spyOn(window, "showInformationMessage")
111+
.mockResolvedValue(undefined);
100112
generateSpy = jest
101113
.spyOn(QlPackGenerator.prototype, "generate")
102114
.mockResolvedValue(undefined);
@@ -168,9 +180,32 @@ describe("SkeletonQueryWizard", () => {
168180
expect(generateSpy).toHaveBeenCalled();
169181
});
170182

171-
it("should download database for selected language", async () => {
183+
it("should prompt for download database", async () => {
172184
await wizard.execute();
173185

186+
expect(showInformationMessageSpy).toHaveBeenCalledWith(
187+
expect.stringMatching(/a CodeQL database/i),
188+
expect.objectContaining({
189+
title: "Download database",
190+
}),
191+
);
192+
expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled();
193+
});
194+
195+
it("should download database for selected language when selecting download in prompt", async () => {
196+
showInformationMessageSpy.mockImplementation(
197+
async (_message, options, item) => {
198+
if (item === undefined) {
199+
return options as MessageItem;
200+
}
201+
202+
return item;
203+
},
204+
);
205+
206+
await wizard.execute();
207+
await wizard.waitForDownload();
208+
174209
expect(downloadGitHubDatabaseSpy).toHaveBeenCalled();
175210
});
176211

@@ -259,51 +294,126 @@ describe("SkeletonQueryWizard", () => {
259294
name: databaseNwo,
260295
language: chosenLanguage,
261296
} as DatabaseItem;
297+
});
262298

263-
mockDatabaseManagerWithItems = mockedObject<DatabaseManager>({
264-
setCurrentDatabaseItem: jest.fn(),
265-
databaseItems: [databaseItem] as DatabaseItem[],
299+
describe("with database selected", () => {
300+
beforeEach(async () => {
301+
mockDatabaseManagerWithItems = mockedObject<DatabaseManager>({
302+
currentDatabaseItem: databaseItem,
303+
setCurrentDatabaseItem: jest.fn(),
304+
databaseItems: [databaseItem] as DatabaseItem[],
305+
});
306+
307+
wizard = new SkeletonQueryWizard(
308+
mockCli,
309+
jest.fn(),
310+
credentials,
311+
extLogger,
312+
mockDatabaseManagerWithItems,
313+
storagePath,
314+
);
266315
});
267316

268-
wizard = new SkeletonQueryWizard(
269-
mockCli,
270-
jest.fn(),
271-
credentials,
272-
extLogger,
273-
mockDatabaseManagerWithItems,
274-
storagePath,
275-
);
276-
});
317+
it("should not download a new database for language", async () => {
318+
await wizard.execute();
277319

278-
it("should not download a new database for language", async () => {
279-
await wizard.execute();
320+
expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled();
321+
});
280322

281-
expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled();
282-
});
323+
it("should not select the database", async () => {
324+
await wizard.execute();
283325

284-
it("should select an existing database", async () => {
285-
await wizard.execute();
326+
expect(
327+
mockDatabaseManagerWithItems.setCurrentDatabaseItem,
328+
).not.toHaveBeenCalled();
329+
});
330+
331+
it("should open the new query file", async () => {
332+
await wizard.execute();
333+
334+
expect(openTextDocumentSpy).toHaveBeenCalledWith(
335+
expect.objectContaining({
336+
path: expect.stringMatching("example2.ql"),
337+
}),
338+
);
339+
});
340+
341+
it("should not show an information message", async () => {
342+
await wizard.execute();
286343

287-
expect(
288-
mockDatabaseManagerWithItems.setCurrentDatabaseItem,
289-
).toHaveBeenCalledWith(databaseItem);
344+
expect(showInformationMessageSpy).not.toHaveBeenCalled();
345+
});
290346
});
291347

292-
it("should open the new query file", async () => {
293-
await wizard.execute();
348+
describe("with database not selected", () => {
349+
beforeEach(async () => {
350+
mockDatabaseManagerWithItems = mockedObject<DatabaseManager>({
351+
currentDatabaseItem: undefined,
352+
setCurrentDatabaseItem: jest.fn(),
353+
databaseItems: [databaseItem] as DatabaseItem[],
354+
});
355+
356+
wizard = new SkeletonQueryWizard(
357+
mockCli,
358+
jest.fn(),
359+
credentials,
360+
extLogger,
361+
mockDatabaseManagerWithItems,
362+
storagePath,
363+
);
364+
});
294365

295-
expect(openTextDocumentSpy).toHaveBeenCalledWith(
296-
expect.objectContaining({
297-
path: expect.stringMatching("example2.ql"),
298-
}),
299-
);
366+
it("should not download a new database for language", async () => {
367+
await wizard.execute();
368+
369+
expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled();
370+
});
371+
372+
it("should select an existing database", async () => {
373+
await wizard.execute();
374+
375+
expect(
376+
mockDatabaseManagerWithItems.setCurrentDatabaseItem,
377+
).toHaveBeenCalledWith(databaseItem);
378+
});
379+
380+
it("should open the new query file", async () => {
381+
await wizard.execute();
382+
383+
expect(openTextDocumentSpy).toHaveBeenCalledWith(
384+
expect.objectContaining({
385+
path: expect.stringMatching("example2.ql"),
386+
}),
387+
);
388+
});
389+
390+
it("should show an information message", async () => {
391+
await wizard.execute();
392+
393+
expect(showInformationMessageSpy).toHaveBeenCalledWith(
394+
expect.stringMatching(new RegExp(databaseNwo)),
395+
);
396+
});
300397
});
301398
});
302399

303400
describe("if database is missing", () => {
304-
describe("if the user choses to downloaded the suggested database from GitHub", () => {
401+
describe("if the user chooses to downloaded the suggested database from GitHub", () => {
402+
beforeEach(() => {
403+
showInformationMessageSpy.mockImplementation(
404+
async (_message, options, item) => {
405+
if (item === undefined) {
406+
return options as MessageItem;
407+
}
408+
409+
return item;
410+
},
411+
);
412+
});
413+
305414
it("should download a new database for language", async () => {
306415
await wizard.execute();
416+
await wizard.waitForDownload();
307417

308418
expect(askForGitHubRepoSpy).toHaveBeenCalled();
309419
expect(downloadGitHubDatabaseSpy).toHaveBeenCalled();
@@ -312,6 +422,16 @@ describe("SkeletonQueryWizard", () => {
312422

313423
describe("if the user choses to download a different database from GitHub than the one suggested", () => {
314424
beforeEach(() => {
425+
showInformationMessageSpy.mockImplementation(
426+
async (_message, options, item) => {
427+
if (item === undefined) {
428+
return options as MessageItem;
429+
}
430+
431+
return item;
432+
},
433+
);
434+
315435
const chosenGitHubRepo = "pickles-owner/pickles-repo";
316436

317437
askForGitHubRepoSpy = jest
@@ -321,6 +441,7 @@ describe("SkeletonQueryWizard", () => {
321441

322442
it("should download the newly chosen database", async () => {
323443
await wizard.execute();
444+
await wizard.waitForDownload();
324445

325446
expect(askForGitHubRepoSpy).toHaveBeenCalled();
326447
expect(downloadGitHubDatabaseSpy).toHaveBeenCalled();

0 commit comments

Comments
 (0)