diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/contextual/queryResolver.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/contextual/queryResolver.test.ts index 57aadd9d4..d06f9a51f 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/contextual/queryResolver.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/contextual/queryResolver.test.ts @@ -13,8 +13,6 @@ import { CodeQLCliServer } from "../../../../src/cli"; import { DatabaseItem } from "../../../../src/databases"; describe("queryResolver", () => { - let writeFileSpy: jest.SpiedFunction; - let getQlPackForDbschemeSpy: jest.SpiedFunction< typeof helpers.getQlPackForDbscheme >; @@ -30,10 +28,6 @@ describe("queryResolver", () => { }; beforeEach(() => { - writeFileSpy = jest - .spyOn(fs, "writeFile") - .mockImplementation(() => Promise.resolve()); - getQlPackForDbschemeSpy = jest .spyOn(helpers, "getQlPackForDbscheme") .mockResolvedValue({ @@ -61,13 +55,15 @@ describe("queryResolver", () => { KeyType.DefinitionQuery, ); expect(result).toEqual(["a", "b"]); - expect(writeFileSpy).toHaveBeenNthCalledWith( - 1, - expect.stringMatching(/.qls$/), - expect.anything(), - expect.anything(), + + expect(mockCli.resolveQueriesInSuite).toHaveBeenCalledWith( + expect.stringMatching(/\.qls$/), + [], ); - expect(load(writeFileSpy.mock.calls[0][1])).toEqual([ + + const fileName = mockCli.resolveQueriesInSuite.mock.calls[0][0]; + + expect(load(await fs.readFile(fileName, "utf-8"))).toEqual([ { from: "my-qlpack", queries: ".", @@ -95,13 +91,15 @@ describe("queryResolver", () => { KeyType.DefinitionQuery, ); expect(result).toEqual(["a", "b"]); - expect(writeFileSpy).toHaveBeenNthCalledWith( - 1, - expect.stringMatching(/.qls$/), - expect.anything(), - expect.anything(), + + expect(mockCli.resolveQueriesInSuite).toHaveBeenCalledWith( + expect.stringMatching(/\.qls$/), + [], ); - expect(load(writeFileSpy.mock.calls[0][1])).toEqual([ + + const fileName = mockCli.resolveQueriesInSuite.mock.calls[0][0]; + + expect(load(await fs.readFile(fileName, "utf-8"))).toEqual([ { from: "my-qlpack2", queries: ".", diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/distribution.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/distribution.test.ts index ba0532315..131ef4699 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/distribution.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/distribution.test.ts @@ -1,11 +1,12 @@ -import { sep } from "path"; import * as fetch from "node-fetch"; import { Range } from "semver"; import * as helpers from "../../../src/helpers"; import { extLogger } from "../../../src/common"; import * as fs from "fs-extra"; +import * as path from "path"; import * as os from "os"; +import * as tmp from "tmp-promise"; import { GithubRelease, GithubReleaseAsset, @@ -13,6 +14,17 @@ import { getExecutableFromDirectory, DistributionManager, } from "../../../src/distribution"; +import { DirectoryResult } from "tmp-promise"; + +jest.mock("os", () => { + const original = jest.requireActual("os"); + return { + ...original, + platform: jest.fn(), + }; +}); + +const mockedOS = jest.mocked(os); describe("Releases API consumer", () => { const owner = "someowner"; @@ -192,17 +204,16 @@ describe("Releases API consumer", () => { }); describe("Launcher path", () => { - const pathToCmd = `abc${sep}codeql.cmd`; - const pathToExe = `abc${sep}codeql.exe`; - let warnSpy: jest.SpiedFunction; let errorSpy: jest.SpiedFunction; let logSpy: jest.SpiedFunction; - let pathExistsSpy: jest.SpiedFunction; - let launcherThatExists = ""; + let directory: DirectoryResult; - beforeEach(() => { + let pathToCmd: string; + let pathToExe: string; + + beforeEach(async () => { warnSpy = jest .spyOn(helpers, "showAndLogWarningMessage") .mockResolvedValue(undefined); @@ -210,22 +221,25 @@ describe("Launcher path", () => { .spyOn(helpers, "showAndLogErrorMessage") .mockResolvedValue(undefined); logSpy = jest.spyOn(extLogger, "log").mockResolvedValue(undefined); - pathExistsSpy = jest - .spyOn(fs, "pathExists") - .mockImplementation(async (path: string) => { - return path.endsWith(launcherThatExists); - }); - jest.spyOn(os, "platform").mockReturnValue("win32"); + mockedOS.platform.mockReturnValue("win32"); + + directory = await tmp.dir({ + unsafeCleanup: true, + }); + + pathToCmd = path.join(directory.path, "codeql.cmd"); + pathToExe = path.join(directory.path, "codeql.exe"); + }); + + afterEach(async () => { + await directory.cleanup(); }); it("should not warn with proper launcher name", async () => { - launcherThatExists = "codeql.exe"; - const result = await getExecutableFromDirectory("abc"); - expect(pathExistsSpy).toBeCalledWith(pathToExe); + await fs.writeFile(pathToExe, ""); - // correct launcher has been found, so alternate one not looked for - expect(pathExistsSpy).not.toBeCalledWith(pathToCmd); + const result = await getExecutableFromDirectory(directory.path); // no warning message expect(warnSpy).not.toHaveBeenCalled(); @@ -235,10 +249,9 @@ describe("Launcher path", () => { }); it("should warn when using a hard-coded deprecated launcher name", async () => { - launcherThatExists = "codeql.cmd"; - const result = await getExecutableFromDirectory("abc"); - expect(pathExistsSpy).toBeCalledWith(pathToExe); - expect(pathExistsSpy).toBeCalledWith(pathToCmd); + await fs.writeFile(pathToCmd, ""); + + const result = await getExecutableFromDirectory(directory.path); // Should have opened a warning message expect(warnSpy).toHaveBeenCalled(); @@ -248,10 +261,7 @@ describe("Launcher path", () => { }); it("should avoid warn when no launcher is found", async () => { - launcherThatExists = "xxx"; - const result = await getExecutableFromDirectory("abc", false); - expect(pathExistsSpy).toBeCalledWith(pathToExe); - expect(pathExistsSpy).toBeCalledWith(pathToCmd); + const result = await getExecutableFromDirectory(directory.path, false); // no warning message expect(warnSpy).not.toHaveBeenCalled(); @@ -261,10 +271,7 @@ describe("Launcher path", () => { }); it("should warn when no launcher is found", async () => { - launcherThatExists = "xxx"; const result = await getExecutableFromDirectory("abc", true); - expect(pathExistsSpy).toBeCalledWith(pathToExe); - expect(pathExistsSpy).toBeCalledWith(pathToCmd); // no warning message expect(warnSpy).not.toHaveBeenCalled(); @@ -274,12 +281,13 @@ describe("Launcher path", () => { }); it("should not warn when deprecated launcher is used, but no new launcher is available", async function () { + await fs.writeFile(pathToCmd, ""); + const manager = new DistributionManager( { customCodeQlPath: pathToCmd } as any, {} as any, undefined as any, ); - launcherThatExists = "codeql.cmd"; const result = await manager.getCodeQlPathWithoutVersionCheck(); expect(result).toBe(pathToCmd); @@ -290,12 +298,14 @@ describe("Launcher path", () => { }); it("should warn when deprecated launcher is used, and new launcher is available", async () => { + await fs.writeFile(pathToCmd, ""); + await fs.writeFile(pathToExe, ""); + const manager = new DistributionManager( { customCodeQlPath: pathToCmd } as any, {} as any, undefined as any, ); - launcherThatExists = ""; // pretend both launchers exist const result = await manager.getCodeQlPathWithoutVersionCheck(); expect(result).toBe(pathToCmd); @@ -311,7 +321,6 @@ describe("Launcher path", () => { {} as any, undefined as any, ); - launcherThatExists = "xxx"; // pretend neither launcher exists const result = await manager.getCodeQlPathWithoutVersionCheck(); expect(result).toBeUndefined(); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts index f2d9dda3e..52116ae36 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts @@ -1,5 +1,8 @@ import { QuickPickItem, window } from "vscode"; -import * as fs from "fs-extra"; +import { join } from "path"; +import { DirectoryResult } from "tmp-promise"; +import * as tmp from "tmp-promise"; +import { ensureDir, writeFile, writeJson } from "fs-extra"; import { UserCancellationException } from "../../../../src/commandRunner"; import * as config from "../../../../src/config"; @@ -126,10 +129,6 @@ describe("repository selection", () => { typeof config.getRemoteRepositoryListsPath >; - let pathExistsStub: jest.SpiedFunction; - let fsStatStub: jest.SpiedFunction; - let fsReadFileStub: jest.SpiedFunction; - beforeEach(() => { quickPickSpy = jest .spyOn(window, "showQuickPick") @@ -144,16 +143,6 @@ describe("repository selection", () => { getRemoteRepositoryListsPathSpy = jest .spyOn(config, "getRemoteRepositoryListsPath") .mockReturnValue(undefined); - - pathExistsStub = jest - .spyOn(fs, "pathExists") - .mockImplementation(() => false); - fsStatStub = jest - .spyOn(fs, "stat") - .mockRejectedValue(new Error("not found")); - fsReadFileStub = jest - .spyOn(fs, "readFile") - .mockRejectedValue(new Error("not found")); }); describe("repo lists from settings", () => { it("should allow selection from repo lists from your pre-defined config", async () => { @@ -362,21 +351,31 @@ describe("repository selection", () => { }); describe("external repository lists file", () => { + let directory: DirectoryResult; + + beforeEach(async () => { + directory = await tmp.dir({ + unsafeCleanup: true, + }); + }); + + afterEach(async () => { + await directory.cleanup(); + }); + it("should fail if path does not exist", async () => { - const fakeFilePath = "/path/that/does/not/exist.json"; - getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath); - pathExistsStub.mockImplementation(() => false); + const nonExistingFile = join(directory.path, "non-existing-file.json"); + getRemoteRepositoryListsPathSpy.mockReturnValue(nonExistingFile); await expect(getRepositorySelection()).rejects.toThrow( - `External repository lists file does not exist at ${fakeFilePath}`, + `External repository lists file does not exist at ${nonExistingFile}`, ); }); it("should fail if path points to directory", async () => { - const fakeFilePath = "/path/to/dir"; - getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath); - pathExistsStub.mockImplementation(() => true); - fsStatStub.mockResolvedValue({ isDirectory: () => true } as any); + const existingDirectory = join(directory.path, "directory"); + await ensureDir(existingDirectory); + getRemoteRepositoryListsPathSpy.mockReturnValue(existingDirectory); await expect(getRepositorySelection()).rejects.toThrow( "External repository lists path should not point to a directory", @@ -384,11 +383,9 @@ describe("repository selection", () => { }); it("should fail if file does not have valid JSON", async () => { - const fakeFilePath = "/path/to/file.json"; - getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath); - pathExistsStub.mockImplementation(() => true); - fsStatStub.mockResolvedValue({ isDirectory: () => false } as any); - fsReadFileStub.mockResolvedValue("not-json" as any as Buffer); + const existingFile = join(directory.path, "repository-lists.json"); + await writeFile(existingFile, "not-json"); + getRemoteRepositoryListsPathSpy.mockReturnValue(existingFile); await expect(getRepositorySelection()).rejects.toThrow( "Invalid repository lists file. It should contain valid JSON.", @@ -396,11 +393,9 @@ describe("repository selection", () => { }); it("should fail if file contains array", async () => { - const fakeFilePath = "/path/to/file.json"; - getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath); - pathExistsStub.mockImplementation(() => true); - fsStatStub.mockResolvedValue({ isDirectory: () => false } as any); - fsReadFileStub.mockResolvedValue("[]" as any as Buffer); + const existingFile = join(directory.path, "repository-lists.json"); + await writeJson(existingFile, []); + getRemoteRepositoryListsPathSpy.mockReturnValue(existingFile); await expect(getRepositorySelection()).rejects.toThrow( "Invalid repository lists file. It should be an object mapping names to a list of repositories.", @@ -408,16 +403,12 @@ describe("repository selection", () => { }); it("should fail if file does not contain repo lists in the right format", async () => { - const fakeFilePath = "/path/to/file.json"; - getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath); - pathExistsStub.mockImplementation(() => true); - fsStatStub.mockResolvedValue({ isDirectory: () => false } as any); + const existingFile = join(directory.path, "repository-lists.json"); const repoLists = { list1: "owner1/repo1", }; - fsReadFileStub.mockResolvedValue( - JSON.stringify(repoLists) as any as Buffer, - ); + await writeJson(existingFile, repoLists); + getRemoteRepositoryListsPathSpy.mockReturnValue(existingFile); await expect(getRepositorySelection()).rejects.toThrow( "Invalid repository lists file. It should contain an array of repositories for each list.", @@ -425,17 +416,13 @@ describe("repository selection", () => { }); it("should get repo lists from file", async () => { - const fakeFilePath = "/path/to/file.json"; - getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath); - pathExistsStub.mockImplementation(() => true); - fsStatStub.mockResolvedValue({ isDirectory: () => false } as any); + const existingFile = join(directory.path, "repository-lists.json"); const repoLists = { list1: ["owner1/repo1", "owner2/repo2"], list2: ["owner3/repo3"], }; - fsReadFileStub.mockResolvedValue( - JSON.stringify(repoLists) as any as Buffer, - ); + await writeJson(existingFile, repoLists); + getRemoteRepositoryListsPathSpy.mockReturnValue(existingFile); getRemoteRepositoryListsSpy.mockReturnValue({ list3: ["onwer4/repo4"], list4: [], diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/test-adapter.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/test-adapter.test.ts index 423224f15..202f26a45 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/test-adapter.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/test-adapter.test.ts @@ -10,6 +10,16 @@ import { FullDatabaseOptions, } from "../../../src/databases"; +jest.mock("fs-extra", () => { + const original = jest.requireActual("fs-extra"); + return { + ...original, + access: jest.fn(), + }; +}); + +const mockedFsExtra = jest.mocked(fs); + describe("test-adapter", () => { let adapter: QLTestAdapter; let fakeDatabaseManager: DatabaseManager; @@ -118,7 +128,7 @@ describe("test-adapter", () => { }); it("should reregister testproj databases around test run", async () => { - jest.spyOn(fs, "access").mockResolvedValue(undefined); + mockedFsExtra.access.mockResolvedValue(undefined); currentDatabaseItem = preTestDatabaseItem; databaseItems = [preTestDatabaseItem]; diff --git a/extensions/ql-vscode/tsconfig.json b/extensions/ql-vscode/tsconfig.json index 39bfe416d..19759b3dc 100644 --- a/extensions/ql-vscode/tsconfig.json +++ b/extensions/ql-vscode/tsconfig.json @@ -20,7 +20,8 @@ "noUnusedLocals": true, "noUnusedParameters": true, "esModuleInterop": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "noEmit": true }, "include": ["src/**/*.ts"], "exclude": ["node_modules", "test", "**/view"]