Move dialog functions to separate file

This commit is contained in:
Koen Vlaswinkel
2023-06-12 12:09:36 +02:00
parent 0cfbf0cb2a
commit 7f3e9607aa
14 changed files with 331 additions and 328 deletions

View File

@@ -0,0 +1,135 @@
import { env, Uri, window } from "vscode";
/**
* Opens a modal dialog for the user to make a yes/no choice.
*
* @param message The message to show.
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
* be closed even if the user does not make a choice.
* @param yesTitle The text in the box indicating the affirmative choice.
* @param noTitle The text in the box indicating the negative choice.
*
* @return
* `true` if the user clicks 'Yes',
* `false` if the user clicks 'No' or cancels the dialog,
* `undefined` if the dialog is closed without the user making a choice.
*/
export async function showBinaryChoiceDialog(
message: string,
modal = true,
yesTitle = "Yes",
noTitle = "No",
): Promise<boolean | undefined> {
const yesItem = { title: yesTitle, isCloseAffordance: false };
const noItem = { title: noTitle, isCloseAffordance: true };
const chosenItem = await window.showInformationMessage(
message,
{ modal },
yesItem,
noItem,
);
if (!chosenItem) {
return undefined;
}
return chosenItem?.title === yesItem.title;
}
/**
* Opens a modal dialog for the user to make a yes/no choice.
*
* @param message The message to show.
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
* be closed even if the user does not make a choice.
*
* @return
* `true` if the user clicks 'Yes',
* `false` if the user clicks 'No' or cancels the dialog,
* `undefined` if the dialog is closed without the user making a choice.
*/
export async function showBinaryChoiceWithUrlDialog(
message: string,
url: string,
): Promise<boolean | undefined> {
const urlItem = { title: "More Information", isCloseAffordance: false };
const yesItem = { title: "Yes", isCloseAffordance: false };
const noItem = { title: "No", isCloseAffordance: true };
let chosenItem;
// Keep the dialog open as long as the user is clicking the 'more information' option.
// To prevent an infinite loop, if the user clicks 'more information' 5 times, close the dialog and return cancelled
let count = 0;
do {
chosenItem = await window.showInformationMessage(
message,
{ modal: true },
urlItem,
yesItem,
noItem,
);
if (chosenItem === urlItem) {
await env.openExternal(Uri.parse(url, true));
}
count++;
} while (chosenItem === urlItem && count < 5);
if (!chosenItem || chosenItem.title === urlItem.title) {
return undefined;
}
return chosenItem.title === yesItem.title;
}
/**
* Show an information message with a customisable action.
* @param message The message to show.
* @param actionMessage The call to action message.
*
* @return `true` if the user clicks the action, `false` if the user cancels the dialog.
*/
export async function showInformationMessageWithAction(
message: string,
actionMessage: string,
): Promise<boolean> {
const actionItem = { title: actionMessage, isCloseAffordance: false };
const chosenItem = await window.showInformationMessage(message, actionItem);
return chosenItem === actionItem;
}
/**
* Opens a modal dialog for the user to make a choice between yes/no/never be asked again.
*
* @param message The message to show.
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
* be closed even if the user does not make a choice.
* @param yesTitle The text in the box indicating the affirmative choice.
* @param noTitle The text in the box indicating the negative choice.
* @param neverTitle The text in the box indicating the opt out choice.
*
* @return
* `Yes` if the user clicks 'Yes',
* `No` if the user clicks 'No' or cancels the dialog,
* `No, and never ask me again` if the user clicks 'No, and never ask me again',
* `undefined` if the dialog is closed without the user making a choice.
*/
export async function showNeverAskAgainDialog(
message: string,
modal = true,
yesTitle = "Yes",
noTitle = "No",
neverAskAgainTitle = "No, and never ask me again",
): Promise<string | undefined> {
const yesItem = { title: yesTitle, isCloseAffordance: true };
const noItem = { title: noTitle, isCloseAffordance: false };
const neverAskAgainItem = {
title: neverAskAgainTitle,
isCloseAffordance: false,
};
const chosenItem = await window.showInformationMessage(
message,
{ modal },
yesItem,
noItem,
neverAskAgainItem,
);
return chosenItem?.title;
}

View File

@@ -1,9 +1,7 @@
import { Uri, window } from "vscode";
import { AppCommandManager } from "../commands";
import {
showAndLogExceptionWithTelemetry,
showBinaryChoiceDialog,
} from "../../helpers";
import { showAndLogExceptionWithTelemetry } from "../../helpers";
import { showBinaryChoiceDialog } from "./dialog";
import { redactableError } from "../../pure/errors";
import {
asError,

View File

@@ -13,10 +13,8 @@ import {
import { join } from "path";
import { FullDatabaseOptions } from "./database-options";
import { DatabaseItemImpl } from "./database-item-impl";
import {
showAndLogExceptionWithTelemetry,
showNeverAskAgainDialog,
} from "../../helpers";
import { showAndLogExceptionWithTelemetry } from "../../helpers";
import { showNeverAskAgainDialog } from "../../common/vscode/dialog";
import {
getFirstWorkspaceFolder,
isFolderAlreadyInWorkspace,

View File

@@ -60,12 +60,14 @@ import {
showAndLogExceptionWithTelemetry,
showAndLogInformationMessage,
showAndLogWarningMessage,
showBinaryChoiceDialog,
showInformationMessageWithAction,
tmpDir,
tmpDirDisposal,
prepareCodeTour,
} from "./helpers";
import {
showBinaryChoiceDialog,
showInformationMessageWithAction,
} from "./common/vscode/dialog";
import {
asError,
assertNever,

View File

@@ -1,7 +1,7 @@
import { ensureDirSync, pathExists, ensureDir, writeFile } from "fs-extra";
import { join } from "path";
import { dirSync } from "tmp-promise";
import { Uri, window as Window, workspace, env } from "vscode";
import { Uri, window as Window, workspace } from "vscode";
import { CodeQLCliServer } from "./codeql-cli/cli";
import { UserCancellationException } from "./common/vscode/progress";
import { extLogger, OutputChannelLogger } from "./common";
@@ -12,6 +12,7 @@ import { isQueryLanguage, QueryLanguage } from "./common/query-language";
import { isCodespacesTemplate } from "./config";
import { AppCommandManager } from "./common/commands";
import { getOnDiskWorkspaceFolders } from "./common/vscode/workspace-folders";
import { showBinaryChoiceDialog } from "./common/vscode/dialog";
// Shared temporary folder for the extension.
export const tmpDir = dirSync({
@@ -139,140 +140,6 @@ async function internalShowAndLog(
return result;
}
/**
* Opens a modal dialog for the user to make a yes/no choice.
*
* @param message The message to show.
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
* be closed even if the user does not make a choice.
* @param yesTitle The text in the box indicating the affirmative choice.
* @param noTitle The text in the box indicating the negative choice.
*
* @return
* `true` if the user clicks 'Yes',
* `false` if the user clicks 'No' or cancels the dialog,
* `undefined` if the dialog is closed without the user making a choice.
*/
export async function showBinaryChoiceDialog(
message: string,
modal = true,
yesTitle = "Yes",
noTitle = "No",
): Promise<boolean | undefined> {
const yesItem = { title: yesTitle, isCloseAffordance: false };
const noItem = { title: noTitle, isCloseAffordance: true };
const chosenItem = await Window.showInformationMessage(
message,
{ modal },
yesItem,
noItem,
);
if (!chosenItem) {
return undefined;
}
return chosenItem?.title === yesItem.title;
}
/**
* Opens a modal dialog for the user to make a yes/no choice.
*
* @param message The message to show.
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
* be closed even if the user does not make a choice.
*
* @return
* `true` if the user clicks 'Yes',
* `false` if the user clicks 'No' or cancels the dialog,
* `undefined` if the dialog is closed without the user making a choice.
*/
export async function showBinaryChoiceWithUrlDialog(
message: string,
url: string,
): Promise<boolean | undefined> {
const urlItem = { title: "More Information", isCloseAffordance: false };
const yesItem = { title: "Yes", isCloseAffordance: false };
const noItem = { title: "No", isCloseAffordance: true };
let chosenItem;
// Keep the dialog open as long as the user is clicking the 'more information' option.
// To prevent an infinite loop, if the user clicks 'more information' 5 times, close the dialog and return cancelled
let count = 0;
do {
chosenItem = await Window.showInformationMessage(
message,
{ modal: true },
urlItem,
yesItem,
noItem,
);
if (chosenItem === urlItem) {
await env.openExternal(Uri.parse(url, true));
}
count++;
} while (chosenItem === urlItem && count < 5);
if (!chosenItem || chosenItem.title === urlItem.title) {
return undefined;
}
return chosenItem.title === yesItem.title;
}
/**
* Show an information message with a customisable action.
* @param message The message to show.
* @param actionMessage The call to action message.
*
* @return `true` if the user clicks the action, `false` if the user cancels the dialog.
*/
export async function showInformationMessageWithAction(
message: string,
actionMessage: string,
): Promise<boolean> {
const actionItem = { title: actionMessage, isCloseAffordance: false };
const chosenItem = await Window.showInformationMessage(message, actionItem);
return chosenItem === actionItem;
}
/**
* Opens a modal dialog for the user to make a choice between yes/no/never be asked again.
*
* @param message The message to show.
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
* be closed even if the user does not make a choice.
* @param yesTitle The text in the box indicating the affirmative choice.
* @param noTitle The text in the box indicating the negative choice.
* @param neverTitle The text in the box indicating the opt out choice.
*
* @return
* `Yes` if the user clicks 'Yes',
* `No` if the user clicks 'No' or cancels the dialog,
* `No, and never ask me again` if the user clicks 'No, and never ask me again',
* `undefined` if the dialog is closed without the user making a choice.
*/
export async function showNeverAskAgainDialog(
message: string,
modal = true,
yesTitle = "Yes",
noTitle = "No",
neverAskAgainTitle = "No, and never ask me again",
): Promise<string | undefined> {
const yesItem = { title: yesTitle, isCloseAffordance: true };
const noItem = { title: noTitle, isCloseAffordance: false };
const neverAskAgainItem = {
title: neverAskAgainTitle,
isCloseAffordance: false,
};
const chosenItem = await Window.showInformationMessage(
message,
{ modal },
yesItem,
noItem,
neverAskAgainItem,
);
return chosenItem?.title;
}
/** Check if the current workspace is the CodeTour and open the workspace folder.
* Without this, we can't run the code tour correctly.
**/

View File

@@ -21,8 +21,8 @@ import {
findLanguage,
showAndLogErrorMessage,
showAndLogWarningMessage,
showBinaryChoiceDialog,
} from "../helpers";
import { showBinaryChoiceDialog } from "../common/vscode/dialog";
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
import { displayQuickQuery } from "./quick-query";
import { CoreCompletedQuery, QueryRunner } from "../query-server";

View File

@@ -5,7 +5,7 @@ import { CancellationToken, window as Window, workspace, Uri } from "vscode";
import { LSPErrorCodes, ResponseError } from "vscode-languageclient";
import { CodeQLCliServer } from "../codeql-cli/cli";
import { DatabaseUI } from "../databases/local-databases-ui";
import { showBinaryChoiceDialog } from "../helpers";
import { showBinaryChoiceDialog } from "../common/vscode/dialog";
import { getInitialQueryContents } from "./query-contents";
import { getPrimaryDbscheme, getQlPackForDbscheme } from "../databases/qlpack";
import {

View File

@@ -17,9 +17,11 @@ import {
showAndLogErrorMessage,
showAndLogInformationMessage,
showAndLogWarningMessage,
} from "../helpers";
import {
showBinaryChoiceDialog,
showInformationMessageWithAction,
} from "../helpers";
} from "../common/vscode/dialog";
import { extLogger } from "../common";
import { URLSearchParams } from "url";
import { DisposableObject } from "../pure/disposable-object";

View File

@@ -17,7 +17,7 @@ import {
import * as appInsights from "applicationinsights";
import { extLogger } from "./common";
import { UserCancellationException } from "./common/vscode/progress";
import { showBinaryChoiceWithUrlDialog } from "./helpers";
import { showBinaryChoiceWithUrlDialog } from "./common/vscode/dialog";
import { RedactableError } from "./pure/errors";
import { SemVer } from "semver";

View File

@@ -7,7 +7,7 @@ import {
UserCancellationException,
withProgress,
} from "../common/vscode/progress";
import { showInformationMessageWithAction } from "../helpers";
import { showInformationMessageWithAction } from "../common/vscode/dialog";
import { extLogger } from "../common";
import { createGist } from "./gh-api/gh-api-client";
import {

View File

@@ -20,7 +20,7 @@ import {
} from "../../../src/common/vscode/archive-filesystem-provider";
import { testDisposeHandler } from "../test-dispose-handler";
import { QueryRunner } from "../../../src/query-server/query-runner";
import * as helpers from "../../../src/helpers";
import * as dialog from "../../../src/common/vscode/dialog";
import { Setting } from "../../../src/config";
import { QlPackGenerator } from "../../../src/qlpack-generator";
import { mockedObject } from "../utils/mocking.helpers";
@@ -45,7 +45,7 @@ describe("local databases", () => {
let logSpy: jest.Mock<any, []>;
let showNeverAskAgainDialogSpy: jest.SpiedFunction<
typeof helpers.showNeverAskAgainDialog
typeof dialog.showNeverAskAgainDialog
>;
let dir: tmp.DirResult;
@@ -64,7 +64,7 @@ describe("local databases", () => {
});
showNeverAskAgainDialogSpy = jest
.spyOn(helpers, "showNeverAskAgainDialog")
.spyOn(dialog, "showNeverAskAgainDialog")
.mockResolvedValue("Yes");
extensionContextStoragePath = dir.name;
@@ -652,7 +652,7 @@ describe("local databases", () => {
it("should return early if the user refuses help", async () => {
showNeverAskAgainDialogSpy = jest
.spyOn(helpers, "showNeverAskAgainDialog")
.spyOn(dialog, "showNeverAskAgainDialog")
.mockResolvedValue("No");
await (databaseManager as any).createSkeletonPacks(mockDbItem);
@@ -662,7 +662,7 @@ describe("local databases", () => {
it("should return early and write choice to settings if user wants to never be asked again", async () => {
showNeverAskAgainDialogSpy = jest
.spyOn(helpers, "showNeverAskAgainDialog")
.spyOn(dialog, "showNeverAskAgainDialog")
.mockResolvedValue("No, and never ask me again");
const updateValueSpy = jest.spyOn(Setting.prototype, "updateValue");
@@ -705,7 +705,7 @@ describe("local databases", () => {
it("should exit early", async () => {
showNeverAskAgainDialogSpy = jest
.spyOn(helpers, "showNeverAskAgainDialog")
.spyOn(dialog, "showNeverAskAgainDialog")
.mockResolvedValue("No");
await (databaseManager as any).createSkeletonPacks(mockDbItem);

View File

@@ -0,0 +1,167 @@
import { window } from "vscode";
import {
showBinaryChoiceDialog,
showBinaryChoiceWithUrlDialog,
showInformationMessageWithAction,
showNeverAskAgainDialog,
} from "../../../../../src/common/vscode/dialog";
describe("showBinaryChoiceDialog", () => {
let showInformationMessageSpy: jest.SpiedFunction<
typeof window.showInformationMessage
>;
beforeEach(() => {
showInformationMessageSpy = jest
.spyOn(window, "showInformationMessage")
.mockResolvedValue(undefined);
});
const resolveArg =
(index: number) =>
(...args: any[]) =>
Promise.resolve(args[index]);
it("should show a binary choice dialog and return `yes`", async () => {
// pretend user chooses 'yes'
showInformationMessageSpy.mockImplementationOnce(resolveArg(2));
const val = await showBinaryChoiceDialog("xxx");
expect(val).toBe(true);
});
it("should show a binary choice dialog and return `no`", async () => {
// pretend user chooses 'no'
showInformationMessageSpy.mockImplementationOnce(resolveArg(3));
const val = await showBinaryChoiceDialog("xxx");
expect(val).toBe(false);
});
});
describe("showInformationMessageWithAction", () => {
let showInformationMessageSpy: jest.SpiedFunction<
typeof window.showInformationMessage
>;
beforeEach(() => {
showInformationMessageSpy = jest
.spyOn(window, "showInformationMessage")
.mockResolvedValue(undefined);
});
const resolveArg =
(index: number) =>
(...args: any[]) =>
Promise.resolve(args[index]);
it("should show an info dialog and confirm the action", async () => {
// pretend user chooses to run action
showInformationMessageSpy.mockImplementationOnce(resolveArg(1));
const val = await showInformationMessageWithAction("xxx", "yyy");
expect(val).toBe(true);
});
it("should show an action dialog and avoid choosing the action", async () => {
// pretend user does not choose to run action
showInformationMessageSpy.mockResolvedValueOnce(undefined);
const val = await showInformationMessageWithAction("xxx", "yyy");
expect(val).toBe(false);
});
});
describe("showBinaryChoiceWithUrlDialog", () => {
let showInformationMessageSpy: jest.SpiedFunction<
typeof window.showInformationMessage
>;
beforeEach(() => {
showInformationMessageSpy = jest
.spyOn(window, "showInformationMessage")
.mockResolvedValue(undefined);
});
const resolveArg =
(index: number) =>
(...args: any[]) =>
Promise.resolve(args[index]);
it("should show a binary choice dialog with a url and return `yes`", async () => {
// pretend user clicks on the url twice and then clicks 'yes'
showInformationMessageSpy
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(3));
const val = await showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
expect(val).toBe(true);
});
it("should show a binary choice dialog with a url and return `no`", async () => {
// pretend user clicks on the url twice and then clicks 'no'
showInformationMessageSpy
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(4));
const val = await showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
expect(val).toBe(false);
});
it("should show a binary choice dialog and exit after clcking `more info` 5 times", async () => {
// pretend user clicks on the url twice and then clicks 'no'
showInformationMessageSpy
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2));
const val = await showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
// No choice was made
expect(val).toBeUndefined();
expect(showInformationMessageSpy).toHaveBeenCalledTimes(5);
});
});
describe("showNeverAskAgainDialog", () => {
let showInformationMessageSpy: jest.SpiedFunction<
typeof window.showInformationMessage
>;
beforeEach(() => {
showInformationMessageSpy = jest
.spyOn(window, "showInformationMessage")
.mockResolvedValue(undefined);
});
const resolveArg =
(index: number) =>
(...args: any[]) =>
Promise.resolve(args[index]);
const title =
"We've noticed you don't have a CodeQL pack available to analyze this database. Can we set up a query pack for you?";
it("should show a ternary choice dialog and return `Yes`", async () => {
// pretend user chooses 'Yes'
const yesItem = resolveArg(2);
showInformationMessageSpy.mockImplementationOnce(yesItem);
const answer = await showNeverAskAgainDialog(title);
expect(answer).toBe("Yes");
});
it("should show a ternary choice dialog and return `No`", async () => {
// pretend user chooses 'No'
const noItem = resolveArg(3);
showInformationMessageSpy.mockImplementationOnce(noItem);
const answer = await showNeverAskAgainDialog(title);
expect(answer).toBe("No");
});
it("should show a ternary choice dialog and return `No, and never ask me again`", async () => {
// pretend user chooses 'No, and never ask me again'
const neverAskAgainItem = resolveArg(4);
showInformationMessageSpy.mockImplementationOnce(neverAskAgainItem);
const answer = await showNeverAskAgainDialog(title);
expect(answer).toBe("No, and never ask me again");
});
});

View File

@@ -3,13 +3,7 @@ import * as tmp from "tmp";
import { join } from "path";
import { writeFile, mkdir } from "fs-extra";
import {
prepareCodeTour,
showBinaryChoiceDialog,
showBinaryChoiceWithUrlDialog,
showInformationMessageWithAction,
showNeverAskAgainDialog,
} from "../../../src/helpers";
import { prepareCodeTour } from "../../../src/helpers";
import { reportStreamProgress } from "../../../src/common/vscode/progress";
import { Setting } from "../../../src/config";
import { createMockCommandManager } from "../../__mocks__/commandsMock";
@@ -71,166 +65,6 @@ describe("helpers", () => {
message: "My prefix (Size unknown)",
});
});
describe("showBinaryChoiceDialog", () => {
let showInformationMessageSpy: jest.SpiedFunction<
typeof window.showInformationMessage
>;
beforeEach(() => {
showInformationMessageSpy = jest
.spyOn(window, "showInformationMessage")
.mockResolvedValue(undefined);
});
const resolveArg =
(index: number) =>
(...args: any[]) =>
Promise.resolve(args[index]);
it("should show a binary choice dialog and return `yes`", async () => {
// pretend user chooses 'yes'
showInformationMessageSpy.mockImplementationOnce(resolveArg(2));
const val = await showBinaryChoiceDialog("xxx");
expect(val).toBe(true);
});
it("should show a binary choice dialog and return `no`", async () => {
// pretend user chooses 'no'
showInformationMessageSpy.mockImplementationOnce(resolveArg(3));
const val = await showBinaryChoiceDialog("xxx");
expect(val).toBe(false);
});
});
describe("showInformationMessageWithAction", () => {
let showInformationMessageSpy: jest.SpiedFunction<
typeof window.showInformationMessage
>;
beforeEach(() => {
showInformationMessageSpy = jest
.spyOn(window, "showInformationMessage")
.mockResolvedValue(undefined);
});
const resolveArg =
(index: number) =>
(...args: any[]) =>
Promise.resolve(args[index]);
it("should show an info dialog and confirm the action", async () => {
// pretend user chooses to run action
showInformationMessageSpy.mockImplementationOnce(resolveArg(1));
const val = await showInformationMessageWithAction("xxx", "yyy");
expect(val).toBe(true);
});
it("should show an action dialog and avoid choosing the action", async () => {
// pretend user does not choose to run action
showInformationMessageSpy.mockResolvedValueOnce(undefined);
const val = await showInformationMessageWithAction("xxx", "yyy");
expect(val).toBe(false);
});
});
describe("showBinaryChoiceWithUrlDialog", () => {
let showInformationMessageSpy: jest.SpiedFunction<
typeof window.showInformationMessage
>;
beforeEach(() => {
showInformationMessageSpy = jest
.spyOn(window, "showInformationMessage")
.mockResolvedValue(undefined);
});
const resolveArg =
(index: number) =>
(...args: any[]) =>
Promise.resolve(args[index]);
it("should show a binary choice dialog with a url and return `yes`", async () => {
// pretend user clicks on the url twice and then clicks 'yes'
showInformationMessageSpy
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(3));
const val = await showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
expect(val).toBe(true);
});
it("should show a binary choice dialog with a url and return `no`", async () => {
// pretend user clicks on the url twice and then clicks 'no'
showInformationMessageSpy
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(4));
const val = await showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
expect(val).toBe(false);
});
it("should show a binary choice dialog and exit after clcking `more info` 5 times", async () => {
// pretend user clicks on the url twice and then clicks 'no'
showInformationMessageSpy
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2));
const val = await showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
// No choice was made
expect(val).toBeUndefined();
expect(showInformationMessageSpy).toHaveBeenCalledTimes(5);
});
});
describe("showNeverAskAgainDialog", () => {
let showInformationMessageSpy: jest.SpiedFunction<
typeof window.showInformationMessage
>;
beforeEach(() => {
showInformationMessageSpy = jest
.spyOn(window, "showInformationMessage")
.mockResolvedValue(undefined);
});
const resolveArg =
(index: number) =>
(...args: any[]) =>
Promise.resolve(args[index]);
const title =
"We've noticed you don't have a CodeQL pack available to analyze this database. Can we set up a query pack for you?";
it("should show a ternary choice dialog and return `Yes`", async () => {
// pretend user chooses 'Yes'
const yesItem = resolveArg(2);
showInformationMessageSpy.mockImplementationOnce(yesItem);
const answer = await showNeverAskAgainDialog(title);
expect(answer).toBe("Yes");
});
it("should show a ternary choice dialog and return `No`", async () => {
// pretend user chooses 'No'
const noItem = resolveArg(3);
showInformationMessageSpy.mockImplementationOnce(noItem);
const answer = await showNeverAskAgainDialog(title);
expect(answer).toBe("No");
});
it("should show a ternary choice dialog and return `No, and never ask me again`", async () => {
// pretend user chooses 'No, and never ask me again'
const neverAskAgainItem = resolveArg(4);
showInformationMessageSpy.mockImplementationOnce(neverAskAgainItem);
const answer = await showNeverAskAgainDialog(title);
expect(answer).toBe("No, and never ask me again");
});
});
});
describe("prepareCodeTour", () => {

View File

@@ -22,7 +22,7 @@ import { createMockVariantAnalysisHistoryItem } from "../../../factories/query-h
import { VariantAnalysisHistoryItem } from "../../../../src/query-history/variant-analysis-history-item";
import { QueryStatus } from "../../../../src/query-status";
import { VariantAnalysisStatus } from "../../../../src/variant-analysis/shared/variant-analysis";
import * as helpers from "../../../../src/helpers";
import * as dialog from "../../../../src/common/vscode/dialog";
import { mockedQuickPickItem } from "../../utils/mocking.helpers";
import { createMockQueryHistoryDirs } from "../../../factories/query-history/query-history-dirs";
import { createMockApp } from "../../../__mocks__/appMock";
@@ -318,20 +318,20 @@ describe("QueryHistoryManager", () => {
describe("when the item is a variant analysis", () => {
let showBinaryChoiceDialogSpy: jest.SpiedFunction<
typeof helpers.showBinaryChoiceDialog
typeof dialog.showBinaryChoiceDialog
>;
let showInformationMessageWithActionSpy: jest.SpiedFunction<
typeof helpers.showInformationMessageWithAction
typeof dialog.showInformationMessageWithAction
>;
beforeEach(() => {
// Choose 'Yes' when asked "Are you sure?"
showBinaryChoiceDialogSpy = jest
.spyOn(helpers, "showBinaryChoiceDialog")
.spyOn(dialog, "showBinaryChoiceDialog")
.mockResolvedValue(true);
showInformationMessageWithActionSpy = jest.spyOn(
helpers,
dialog,
"showInformationMessageWithAction",
);
});