Skip to content

Commit 2e7952c

Browse files
Pull tryOpenExternalFile out to a separate file so it can be more easily tested
1 parent 6bcfdda commit 2e7952c

4 files changed

Lines changed: 135 additions & 124 deletions

File tree

extensions/ql-vscode/src/query-history/query-history-manager.ts

Lines changed: 10 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,7 @@ import { extLogger } from "../common";
2626
import { URLSearchParams } from "url";
2727
import { DisposableObject } from "../pure/disposable-object";
2828
import { ONE_HOUR_IN_MS, TWO_HOURS_IN_MS } from "../pure/time";
29-
import {
30-
asError,
31-
assertNever,
32-
getErrorMessage,
33-
getErrorStack,
34-
} from "../pure/helpers-pure";
29+
import { asError, assertNever, getErrorMessage } from "../pure/helpers-pure";
3530
import { CompletedLocalQueryInfo, LocalQueryInfo } from "../query-results";
3631
import {
3732
getActionsWorkflowRunUrl,
@@ -66,6 +61,7 @@ import { HistoryTreeDataProvider } from "./history-tree-data-provider";
6661
import { redactableError } from "../pure/errors";
6762
import { QueryHistoryDirs } from "./query-history-dirs";
6863
import { QueryHistoryCommands } from "../common/commands";
64+
import { tryOpenExternalFile } from "../vscode-utils/external-files";
6965

7066
/**
7167
* query-history-manager.ts
@@ -683,7 +679,7 @@ export class QueryHistoryManager extends DisposableObject {
683679
}
684680

685681
if (singleItem.completedQuery.logFileLocation) {
686-
await this.tryOpenExternalFile(singleItem.completedQuery.logFileLocation);
682+
await tryOpenExternalFile(singleItem.completedQuery.logFileLocation);
687683
} else {
688684
void showAndLogWarningMessage("No log file available");
689685
}
@@ -800,7 +796,7 @@ export class QueryHistoryManager extends DisposableObject {
800796
}
801797

802798
if (finalSingleItem.evalLogLocation) {
803-
await this.tryOpenExternalFile(finalSingleItem.evalLogLocation);
799+
await tryOpenExternalFile(finalSingleItem.evalLogLocation);
804800
} else {
805801
this.warnNoEvalLogs();
806802
}
@@ -825,7 +821,7 @@ export class QueryHistoryManager extends DisposableObject {
825821
}
826822

827823
if (finalSingleItem.evalLogSummaryLocation) {
828-
await this.tryOpenExternalFile(finalSingleItem.evalLogSummaryLocation);
824+
await tryOpenExternalFile(finalSingleItem.evalLogSummaryLocation);
829825
return;
830826
}
831827

@@ -968,7 +964,7 @@ export class QueryHistoryManager extends DisposableObject {
968964
const query = finalSingleItem.completedQuery.query;
969965
const hasInterpretedResults = query.canHaveInterpretedResults();
970966
if (hasInterpretedResults) {
971-
await this.tryOpenExternalFile(query.resultsPaths.interpretedResultsPath);
967+
await tryOpenExternalFile(query.resultsPaths.interpretedResultsPath);
972968
} else {
973969
const label = this.labelProvider.getLabel(finalSingleItem);
974970
void showAndLogInformationMessage(
@@ -997,11 +993,11 @@ export class QueryHistoryManager extends DisposableObject {
997993
}
998994
const query = finalSingleItem.completedQuery.query;
999995
if (await query.hasCsv()) {
1000-
void this.tryOpenExternalFile(query.csvPath);
996+
void tryOpenExternalFile(query.csvPath);
1001997
return;
1002998
}
1003999
if (await query.exportCsvResults(this.qs.cliServer, query.csvPath)) {
1004-
void this.tryOpenExternalFile(query.csvPath);
1000+
void tryOpenExternalFile(query.csvPath);
10051001
}
10061002
}
10071003

@@ -1024,7 +1020,7 @@ export class QueryHistoryManager extends DisposableObject {
10241020
return;
10251021
}
10261022

1027-
await this.tryOpenExternalFile(
1023+
await tryOpenExternalFile(
10281024
await finalSingleItem.completedQuery.query.ensureCsvAlerts(
10291025
this.qs.cliServer,
10301026
this.dbm,
@@ -1051,7 +1047,7 @@ export class QueryHistoryManager extends DisposableObject {
10511047
return;
10521048
}
10531049

1054-
await this.tryOpenExternalFile(
1050+
await tryOpenExternalFile(
10551051
await finalSingleItem.completedQuery.query.ensureDilPath(
10561052
this.qs.cliServer,
10571053
),
@@ -1171,47 +1167,6 @@ export class QueryHistoryManager extends DisposableObject {
11711167
}
11721168
}
11731169

1174-
private async tryOpenExternalFile(fileLocation: string) {
1175-
const uri = Uri.file(fileLocation);
1176-
try {
1177-
await window.showTextDocument(uri, { preview: false });
1178-
} catch (e) {
1179-
const msg = getErrorMessage(e);
1180-
if (
1181-
msg.includes(
1182-
"Files above 50MB cannot be synchronized with extensions",
1183-
) ||
1184-
msg.includes("too large to open")
1185-
) {
1186-
const res = await showBinaryChoiceDialog(
1187-
`VS Code does not allow extensions to open files >50MB. This file
1188-
exceeds that limit. Do you want to open it outside of VS Code?
1189-
1190-
You can also try manually opening it inside VS Code by selecting
1191-
the file in the file explorer and dragging it into the workspace.`,
1192-
);
1193-
if (res) {
1194-
try {
1195-
await commands.executeCommand("revealFileInOS", uri);
1196-
} catch (e) {
1197-
void showAndLogExceptionWithTelemetry(
1198-
redactableError(
1199-
asError(e),
1200-
)`Failed to reveal file in OS: ${getErrorMessage(e)}`,
1201-
);
1202-
}
1203-
}
1204-
} else {
1205-
void showAndLogExceptionWithTelemetry(
1206-
redactableError(asError(e))`Could not open file ${fileLocation}`,
1207-
{
1208-
fullMessage: `${getErrorMessage(e)}\n${getErrorStack(e)}`,
1209-
},
1210-
);
1211-
}
1212-
}
1213-
}
1214-
12151170
private async findOtherQueryToCompare(
12161171
singleItem: QueryHistoryInfo,
12171172
multiSelect: QueryHistoryInfo[],
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { commands, Uri, window } from "vscode";
2+
import {
3+
showAndLogExceptionWithTelemetry,
4+
showBinaryChoiceDialog,
5+
} from "../helpers";
6+
import { redactableError } from "../pure/errors";
7+
import { asError, getErrorMessage, getErrorStack } from "../pure/helpers-pure";
8+
9+
export async function tryOpenExternalFile(fileLocation: string) {
10+
const uri = Uri.file(fileLocation);
11+
try {
12+
await window.showTextDocument(uri, { preview: false });
13+
} catch (e) {
14+
const msg = getErrorMessage(e);
15+
if (
16+
msg.includes("Files above 50MB cannot be synchronized with extensions") ||
17+
msg.includes("too large to open")
18+
) {
19+
const res = await showBinaryChoiceDialog(
20+
`VS Code does not allow extensions to open files >50MB. This file
21+
exceeds that limit. Do you want to open it outside of VS Code?
22+
23+
You can also try manually opening it inside VS Code by selecting
24+
the file in the file explorer and dragging it into the workspace.`,
25+
);
26+
if (res) {
27+
try {
28+
await commands.executeCommand("revealFileInOS", uri);
29+
} catch (e) {
30+
void showAndLogExceptionWithTelemetry(
31+
redactableError(
32+
asError(e),
33+
)`Failed to reveal file in OS: ${getErrorMessage(e)}`,
34+
);
35+
}
36+
}
37+
} else {
38+
void showAndLogExceptionWithTelemetry(
39+
redactableError(asError(e))`Could not open file ${fileLocation}`,
40+
{
41+
fullMessage: `${getErrorMessage(e)}\n${getErrorStack(e)}`,
42+
},
43+
);
44+
}
45+
}
46+
}

extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts

Lines changed: 14 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -22,59 +22,49 @@ import { createMockVariantAnalysisHistoryItem } from "../../../factories/query-h
2222
import { VariantAnalysisHistoryItem } from "../../../../src/query-history/variant-analysis-history-item";
2323
import { QueryStatus } from "../../../../src/query-status";
2424
import { VariantAnalysisStatus } from "../../../../src/variant-analysis/shared/variant-analysis";
25-
import { TextEditor } from "vscode";
2625
import { WebviewReveal } from "../../../../src/interface-utils";
2726
import * as helpers from "../../../../src/helpers";
28-
import { mockedObject, mockedQuickPickItem } from "../../utils/mocking.helpers";
27+
import { mockedQuickPickItem } from "../../utils/mocking.helpers";
2928
import { createMockQueryHistoryDirs } from "../../../factories/query-history/query-history-dirs";
29+
import { createMockApp } from "../../../__mocks__/appMock";
30+
import { App } from "../../../../src/common/app";
31+
import { createMockCommandManager } from "../../../__mocks__/commandsMock";
3032

3133
describe("QueryHistoryManager", () => {
3234
const mockExtensionLocation = join(tmpDir.name, "mock-extension-location");
3335
let configListener: QueryHistoryConfigListener;
34-
let showTextDocumentSpy: jest.SpiedFunction<
35-
typeof vscode.window.showTextDocument
36-
>;
37-
let showInformationMessageSpy: jest.SpiedFunction<
38-
typeof vscode.window.showInformationMessage
39-
>;
4036
let showQuickPickSpy: jest.SpiedFunction<typeof vscode.window.showQuickPick>;
41-
let executeCommandSpy: jest.SpiedFunction<
42-
typeof vscode.commands.executeCommand
43-
>;
4437
let cancelVariantAnalysisSpy: jest.SpiedFunction<
4538
typeof variantAnalysisManagerStub.cancelVariantAnalysis
4639
>;
4740
const doCompareCallback = jest.fn();
4841

42+
let executeCommand: jest.MockedFn<
43+
(commandName: string, ...args: any[]) => Promise<any>
44+
>;
45+
let mockApp: App;
46+
4947
let queryHistoryManager: QueryHistoryManager;
5048

5149
let localQueriesResultsViewStub: ResultsView;
5250
let variantAnalysisManagerStub: VariantAnalysisManager;
5351

54-
let tryOpenExternalFile: Function;
55-
5652
let allHistory: QueryHistoryInfo[];
5753
let localQueryHistory: LocalQueryInfo[];
5854
let variantAnalysisHistory: VariantAnalysisHistoryItem[];
5955

6056
beforeEach(() => {
61-
showTextDocumentSpy = jest
62-
.spyOn(vscode.window, "showTextDocument")
63-
.mockResolvedValue(mockedObject<TextEditor>({}));
64-
showInformationMessageSpy = jest
65-
.spyOn(vscode.window, "showInformationMessage")
66-
.mockResolvedValue(undefined);
6757
showQuickPickSpy = jest
6858
.spyOn(vscode.window, "showQuickPick")
6959
.mockResolvedValue(undefined);
70-
executeCommandSpy = jest
71-
.spyOn(vscode.commands, "executeCommand")
72-
.mockResolvedValue(undefined);
60+
61+
executeCommand = jest.fn();
62+
mockApp = createMockApp({
63+
commands: createMockCommandManager({ executeCommand }),
64+
});
7365

7466
jest.spyOn(extLogger, "log").mockResolvedValue(undefined);
7567

76-
tryOpenExternalFile = (QueryHistoryManager.prototype as any)
77-
.tryOpenExternalFile;
7868
configListener = new QueryHistoryConfigListener();
7969
localQueriesResultsViewStub = {
8070
showResults: jest.fn(),
@@ -157,51 +147,6 @@ describe("QueryHistoryManager", () => {
157147
queryHistoryManager.dispose();
158148
}
159149
});
160-
describe("tryOpenExternalFile", () => {
161-
it("should open an external file", async () => {
162-
await tryOpenExternalFile("xxx");
163-
expect(showTextDocumentSpy).toHaveBeenCalledTimes(1);
164-
expect(showTextDocumentSpy).toHaveBeenCalledWith(
165-
vscode.Uri.file("xxx"),
166-
expect.anything(),
167-
);
168-
expect(executeCommandSpy).not.toBeCalled();
169-
});
170-
171-
[
172-
"too large to open",
173-
"Files above 50MB cannot be synchronized with extensions",
174-
].forEach((msg) => {
175-
it(`should fail to open a file because "${msg}" and open externally`, async () => {
176-
showTextDocumentSpy.mockRejectedValue(new Error(msg));
177-
showInformationMessageSpy.mockResolvedValue({ title: "Yes" });
178-
179-
await tryOpenExternalFile("xxx");
180-
const uri = vscode.Uri.file("xxx");
181-
expect(showTextDocumentSpy).toHaveBeenCalledTimes(1);
182-
expect(showTextDocumentSpy).toHaveBeenCalledWith(
183-
uri,
184-
expect.anything(),
185-
);
186-
expect(executeCommandSpy).toHaveBeenCalledWith("revealFileInOS", uri);
187-
});
188-
189-
it(`should fail to open a file because "${msg}" and NOT open externally`, async () => {
190-
showTextDocumentSpy.mockRejectedValue(new Error(msg));
191-
showInformationMessageSpy.mockResolvedValue({ title: "No" });
192-
193-
await tryOpenExternalFile("xxx");
194-
const uri = vscode.Uri.file("xxx");
195-
expect(showTextDocumentSpy).toHaveBeenCalledTimes(1);
196-
expect(showTextDocumentSpy).toHaveBeenCalledWith(
197-
uri,
198-
expect.anything(),
199-
);
200-
expect(showInformationMessageSpy).toBeCalled();
201-
expect(executeCommandSpy).not.toBeCalled();
202-
});
203-
});
204-
});
205150

206151
describe("handleItemClicked", () => {
207152
describe("single click", () => {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import * as vscode from "vscode";
2+
import { tryOpenExternalFile } from "../../../../../src/vscode-utils/external-files";
3+
import { mockedObject } from "../../../utils/mocking.helpers";
4+
5+
describe("tryOpenExternalFile", () => {
6+
let showTextDocumentSpy: jest.SpiedFunction<
7+
typeof vscode.window.showTextDocument
8+
>;
9+
let showInformationMessageSpy: jest.SpiedFunction<
10+
typeof vscode.window.showInformationMessage
11+
>;
12+
let executeCommandSpy: jest.SpiedFunction<
13+
typeof vscode.commands.executeCommand
14+
>;
15+
16+
beforeEach(() => {
17+
showTextDocumentSpy = jest
18+
.spyOn(vscode.window, "showTextDocument")
19+
.mockResolvedValue(mockedObject<vscode.TextEditor>({}));
20+
showInformationMessageSpy = jest
21+
.spyOn(vscode.window, "showInformationMessage")
22+
.mockResolvedValue(undefined);
23+
executeCommandSpy = jest
24+
.spyOn(vscode.commands, "executeCommand")
25+
.mockResolvedValue(undefined);
26+
});
27+
28+
it("should open an external file", async () => {
29+
await tryOpenExternalFile("xxx");
30+
expect(showTextDocumentSpy).toHaveBeenCalledTimes(1);
31+
expect(showTextDocumentSpy).toHaveBeenCalledWith(
32+
vscode.Uri.file("xxx"),
33+
expect.anything(),
34+
);
35+
expect(executeCommandSpy).not.toBeCalled();
36+
});
37+
38+
[
39+
"too large to open",
40+
"Files above 50MB cannot be synchronized with extensions",
41+
].forEach((msg) => {
42+
it(`should fail to open a file because "${msg}" and open externally`, async () => {
43+
showTextDocumentSpy.mockRejectedValue(new Error(msg));
44+
showInformationMessageSpy.mockResolvedValue({ title: "Yes" });
45+
46+
await tryOpenExternalFile("xxx");
47+
const uri = vscode.Uri.file("xxx");
48+
expect(showTextDocumentSpy).toHaveBeenCalledTimes(1);
49+
expect(showTextDocumentSpy).toHaveBeenCalledWith(uri, expect.anything());
50+
expect(executeCommandSpy).toHaveBeenCalledWith("revealFileInOS", uri);
51+
});
52+
53+
it(`should fail to open a file because "${msg}" and NOT open externally`, async () => {
54+
showTextDocumentSpy.mockRejectedValue(new Error(msg));
55+
showInformationMessageSpy.mockResolvedValue({ title: "No" });
56+
57+
await tryOpenExternalFile("xxx");
58+
const uri = vscode.Uri.file("xxx");
59+
expect(showTextDocumentSpy).toHaveBeenCalledTimes(1);
60+
expect(showTextDocumentSpy).toHaveBeenCalledWith(uri, expect.anything());
61+
expect(showInformationMessageSpy).toBeCalled();
62+
expect(executeCommandSpy).not.toBeCalled();
63+
});
64+
});
65+
});

0 commit comments

Comments
 (0)