Move dialog functions to separate file
This commit is contained in:
135
extensions/ql-vscode/src/common/vscode/dialog.ts
Normal file
135
extensions/ql-vscode/src/common/vscode/dialog.ts
Normal 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;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
**/
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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",
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user