Migrate no-workspace integration tests to Jest
This migrates all no-workspace VSCode integration tests to Jest by running `npx jest-codemods`, followed by manual fixes (mostly for Sinon and Proxyquire).
This commit is contained in:
18
extensions/ql-vscode/jest-runner-vscode.config.js
Normal file
18
extensions/ql-vscode/jest-runner-vscode.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const path = require("path");
|
||||
const tmp = require("tmp-promise");
|
||||
|
||||
const tmpDir = tmp.dirSync({ unsafeCleanup: true });
|
||||
|
||||
/** @type {import('jest-runner-vscode').RunnerOptions} */
|
||||
module.exports = {
|
||||
version: "stable",
|
||||
launchArgs: [
|
||||
"--disable-extensions",
|
||||
"--disable-gpu",
|
||||
"--new-window",
|
||||
"--user-data-dir=" + path.join(tmpDir.name, "user-data"),
|
||||
],
|
||||
workspaceDir: path.resolve(__dirname, "test/data"),
|
||||
openInFolder: true,
|
||||
extensionDevelopmentPath: path.resolve(__dirname),
|
||||
};
|
||||
@@ -4,5 +4,9 @@
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
projects: ["<rootDir>/src/view", "<rootDir>/test"],
|
||||
projects: [
|
||||
"<rootDir>/src/view",
|
||||
"<rootDir>/test",
|
||||
"<rootDir>/out/vscode-tests/no-workspace",
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1272,8 +1272,8 @@
|
||||
"test": "npm-run-all -p test:*",
|
||||
"test:unit": "jest --projects test",
|
||||
"test:view": "jest --projects src/view",
|
||||
"integration": "node ./out/vscode-tests/run-integration-tests.js no-workspace,minimal-workspace",
|
||||
"integration:no-workspace": "node ./out/vscode-tests/run-integration-tests.js no-workspace",
|
||||
"integration": "npm-run-all -p integration:*",
|
||||
"integration:no-workspace": "jest --projects out/vscode-tests/no-workspace",
|
||||
"integration:minimal-workspace": "node ./out/vscode-tests/run-integration-tests.js minimal-workspace",
|
||||
"cli-integration": "node ./out/vscode-tests/run-integration-tests.js cli-integration",
|
||||
"update-vscode": "node ./node_modules/vscode/bin/install",
|
||||
|
||||
@@ -66,7 +66,6 @@ export function createMockLocalQueryInfo({
|
||||
}
|
||||
|
||||
export function createMockQueryWithResults({
|
||||
sandbox = undefined,
|
||||
didRunSuccessfully = true,
|
||||
hasInterpretedResults = true,
|
||||
hasMetadata = undefined,
|
||||
@@ -76,16 +75,8 @@ export function createMockQueryWithResults({
|
||||
hasInterpretedResults?: boolean;
|
||||
hasMetadata?: boolean;
|
||||
}): QueryWithResults {
|
||||
const dispose = sandbox
|
||||
? sandbox.spy()
|
||||
: () => {
|
||||
/**/
|
||||
};
|
||||
const deleteQuery = sandbox
|
||||
? sandbox.stub()
|
||||
: () => {
|
||||
/**/
|
||||
};
|
||||
const dispose = jest.fn();
|
||||
const deleteQuery = jest.fn();
|
||||
const metadata = hasMetadata
|
||||
? ({ name: "query-name" } as QueryMetadata)
|
||||
: undefined;
|
||||
|
||||
205
extensions/ql-vscode/src/vscode-tests/jest.config.base.ts
Normal file
205
extensions/ql-vscode/src/vscode-tests/jest.config.base.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
import type { Config } from "jest";
|
||||
|
||||
/*
|
||||
* For a detailed explanation regarding each configuration property and type check, visit:
|
||||
* https://jestjs.io/docs/configuration
|
||||
*/
|
||||
|
||||
const config: Config = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
// Stop running tests after `n` failures
|
||||
// bail: 0,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
// cacheDirectory: "/private/var/folders/6m/1394pht172qgd7dmw1fwjk100000gn/T/jest_dx",
|
||||
|
||||
// Automatically clear mock calls, instances, contexts and results before every test
|
||||
// clearMocks: true,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
// collectCoverage: false,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
// collectCoverageFrom: undefined,
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
// coverageDirectory: undefined,
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// Indicates which provider should be used to instrument code for coverage
|
||||
coverageProvider: "v8",
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
// coverageReporters: [
|
||||
// "json",
|
||||
// "text",
|
||||
// "lcov",
|
||||
// "clover"
|
||||
// ],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
// coverageThreshold: undefined,
|
||||
|
||||
// A path to a custom dependency extractor
|
||||
// dependencyExtractor: undefined,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// The default configuration for fake timers
|
||||
// fakeTimers: {
|
||||
// "enableGlobally": false
|
||||
// },
|
||||
|
||||
// Force coverage collection from ignored files using an array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: undefined,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: undefined,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
// globals: {},
|
||||
|
||||
// Insert Jest's globals (expect, test, describe, beforeEach etc.) into the global environment. If you set this to false, you should import from @jest/globals.
|
||||
// injectGlobals: false,
|
||||
|
||||
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||
// maxWorkers: 1,
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
moduleFileExtensions: ["js", "mjs", "cjs", "jsx", "ts", "tsx", "json"],
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
// preset: 'ts-jest',
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: undefined,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state before every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: undefined,
|
||||
|
||||
// Automatically restore mock state and implementation before every test
|
||||
// restoreMocks: false,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
// rootDir: undefined,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
// roots: [
|
||||
// "<rootDir>"
|
||||
// ],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
runner: "vscode",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
setupFilesAfterEnv: ["<rootDir>/../jest.setup.js"],
|
||||
|
||||
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
||||
// slowTestThreshold: 5,
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
// testEnvironment: 'jsdom',
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
testMatch: ["**/*.test.[jt]s"],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: undefined,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jest-circus/runner",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
// transform: {
|
||||
// '^.+\\.tsx?$': [
|
||||
// 'ts-jest',
|
||||
// {
|
||||
// tsconfig: 'src/view/tsconfig.spec.json',
|
||||
// },
|
||||
// ],
|
||||
// 'node_modules': [
|
||||
// 'babel-jest',
|
||||
// {
|
||||
// presets: [
|
||||
// '@babel/preset-env'
|
||||
// ],
|
||||
// plugins: [
|
||||
// '@babel/plugin-transform-modules-commonjs',
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// 'transformIgnorePatterns': [
|
||||
// // These use ES modules, so need to be transformed
|
||||
// 'node_modules/(?!(?:@vscode/webview-ui-toolkit|@microsoft/.+|exenv-es6)/.*)'
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: undefined,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
};
|
||||
|
||||
export default config;
|
||||
9
extensions/ql-vscode/src/vscode-tests/jest.setup.ts
Normal file
9
extensions/ql-vscode/src/vscode-tests/jest.setup.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { env } from "vscode";
|
||||
|
||||
(env as any).openExternal = () => {
|
||||
/**/
|
||||
};
|
||||
|
||||
afterAll(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
@@ -1,13 +1,15 @@
|
||||
import * as assert from "assert";
|
||||
// This file needs to be located in a separate directory. This will ensure that this
|
||||
// test is run at the start-up of a new VSCode instance.
|
||||
|
||||
import * as vscode from "vscode";
|
||||
|
||||
// Note that this may open the most recent VSCode workspace.
|
||||
describe("launching with no specified workspace", () => {
|
||||
const ext = vscode.extensions.getExtension("GitHub.vscode-codeql");
|
||||
it("should install the extension", () => {
|
||||
assert(ext);
|
||||
expect(ext).not.toBeUndefined();
|
||||
});
|
||||
it("should not activate the extension at first", () => {
|
||||
assert(ext!.isActive === false);
|
||||
expect(ext!.isActive).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
import { expect } from "chai";
|
||||
import * as path from "path";
|
||||
|
||||
import {
|
||||
@@ -22,7 +21,7 @@ describe("archive-filesystem-provider", () => {
|
||||
pathWithinSourceArchive: "/aFileName.txt",
|
||||
});
|
||||
const data = await archiveProvider.readFile(uri);
|
||||
expect(data.length).to.equal(0);
|
||||
expect(data.length).toBe(0);
|
||||
});
|
||||
|
||||
it("read non-empty file correctly", async () => {
|
||||
@@ -35,7 +34,7 @@ describe("archive-filesystem-provider", () => {
|
||||
pathWithinSourceArchive: "folder1/textFile.txt",
|
||||
});
|
||||
const data = await archiveProvider.readFile(uri);
|
||||
expect(Buffer.from(data).toString("utf8")).to.be.equal("I am a text\n");
|
||||
expect(Buffer.from(data).toString("utf8")).toBe("I am a text\n");
|
||||
});
|
||||
|
||||
it("read a directory", async () => {
|
||||
@@ -48,7 +47,7 @@ describe("archive-filesystem-provider", () => {
|
||||
pathWithinSourceArchive: "folder1",
|
||||
});
|
||||
const files = await archiveProvider.readDirectory(uri);
|
||||
expect(files).to.be.deep.equal([
|
||||
expect(files).toEqual([
|
||||
["folder2", FileType.Directory],
|
||||
["textFile.txt", FileType.File],
|
||||
["textFile2.txt", FileType.File],
|
||||
@@ -68,7 +67,7 @@ describe("archive-filesystem-provider", () => {
|
||||
await archiveProvider.readDirectory(uri);
|
||||
throw new Error("Failed");
|
||||
} catch (e) {
|
||||
expect(e).to.be.instanceOf(FileSystemError);
|
||||
expect(e).toBeInstanceOf(FileSystemError);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -85,7 +84,7 @@ describe("archive-filesystem-provider", () => {
|
||||
await archiveProvider.readFile(uri);
|
||||
throw new Error("Failed");
|
||||
} catch (e) {
|
||||
expect(e).to.be.instanceOf(FileSystemError);
|
||||
expect(e).toBeInstanceOf(FileSystemError);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -102,7 +101,7 @@ describe("archive-filesystem-provider", () => {
|
||||
await archiveProvider.readDirectory(uri);
|
||||
throw new Error("Failed");
|
||||
} catch (e) {
|
||||
expect(e).to.be.instanceOf(FileSystemError);
|
||||
expect(e).toBeInstanceOf(FileSystemError);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -119,7 +118,7 @@ describe("archive-filesystem-provider", () => {
|
||||
await archiveProvider.readFile(uri);
|
||||
throw new Error("Failed");
|
||||
} catch (e) {
|
||||
expect(e).to.be.instanceOf(FileSystemError);
|
||||
expect(e).toBeInstanceOf(FileSystemError);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -133,11 +132,11 @@ describe("archive-filesystem-provider", () => {
|
||||
pathWithinSourceArchive: "folder1/folder2",
|
||||
});
|
||||
const files = await archiveProvider.readDirectory(uri);
|
||||
expect(files).to.be.deep.equal([["textFile3.txt", FileType.File]]);
|
||||
expect(files).toEqual([["textFile3.txt", FileType.File]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("source archive uri encoding", function () {
|
||||
describe("source archive uri encoding", () => {
|
||||
const testCases: { name: string; input: ZipFileReference }[] = [
|
||||
{
|
||||
name: "mixed case and unicode",
|
||||
@@ -169,11 +168,11 @@ describe("source archive uri encoding", function () {
|
||||
},
|
||||
];
|
||||
for (const testCase of testCases) {
|
||||
it(`should work round trip with ${testCase.name}`, function () {
|
||||
it(`should work round trip with ${testCase.name}`, () => {
|
||||
const output = decodeSourceArchiveUri(
|
||||
encodeSourceArchiveUri(testCase.input),
|
||||
);
|
||||
expect(output).to.eql(testCase.input);
|
||||
expect(output).toEqual(testCase.input);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -182,7 +181,7 @@ describe("source archive uri encoding", function () {
|
||||
pathWithinSourceArchive: "",
|
||||
sourceArchiveZipPath: "a/b/c",
|
||||
});
|
||||
expect(decodeSourceArchiveUri(uri)).to.deep.eq({
|
||||
expect(decodeSourceArchiveUri(uri)).toEqual({
|
||||
pathWithinSourceArchive: "/",
|
||||
sourceArchiveZipPath: "a/b/c",
|
||||
});
|
||||
@@ -191,10 +190,10 @@ describe("source archive uri encoding", function () {
|
||||
it("should encode a uri at the root of the archive", () => {
|
||||
const path = "/a/b/c/src.zip";
|
||||
const uri = encodeArchiveBasePath(path);
|
||||
expect(uri.path).to.eq(path);
|
||||
expect(decodeSourceArchiveUri(uri).pathWithinSourceArchive).to.eq("/");
|
||||
expect(decodeSourceArchiveUri(uri).sourceArchiveZipPath).to.eq(path);
|
||||
expect(uri.authority).to.eq("0-14");
|
||||
expect(uri.path).toBe(path);
|
||||
expect(decodeSourceArchiveUri(uri).pathWithinSourceArchive).toBe("/");
|
||||
expect(decodeSourceArchiveUri(uri).sourceArchiveZipPath).toBe(path);
|
||||
expect(uri.authority).toBe("0-14");
|
||||
});
|
||||
|
||||
it("should handle malformed uri with no authority", () => {
|
||||
@@ -202,8 +201,8 @@ describe("source archive uri encoding", function () {
|
||||
const uri = Uri.parse("file:/a/b/c/src.zip").with({
|
||||
scheme: zipArchiveScheme,
|
||||
});
|
||||
expect(uri.authority).to.eq("");
|
||||
expect(decodeSourceArchiveUri(uri)).to.deep.eq({
|
||||
expect(uri.authority).toBe("");
|
||||
expect(decodeSourceArchiveUri(uri)).toEqual({
|
||||
sourceArchiveZipPath: "/a/b/c/src.zip",
|
||||
pathWithinSourceArchive: "/",
|
||||
});
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import * as fs from "fs-extra";
|
||||
import { expect } from "chai";
|
||||
import * as sinon from "sinon";
|
||||
import * as yaml from "js-yaml";
|
||||
|
||||
import { AstViewer, AstItem } from "../../astViewer";
|
||||
@@ -11,20 +9,21 @@ import { testDisposeHandler } from "../test-dispose-handler";
|
||||
describe("AstViewer", () => {
|
||||
let astRoots: AstItem[];
|
||||
let viewer: AstViewer | undefined;
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
beforeEach(async () => {
|
||||
sandbox = sinon.createSandbox();
|
||||
// the ast is stored in yaml because there are back pointers
|
||||
// making a json representation impossible.
|
||||
// The complication here is that yaml files are not copied into the 'out' directory by tsc.
|
||||
astRoots = await buildAst();
|
||||
|
||||
sandbox.stub(commands, "registerCommand");
|
||||
sandbox.stub(commands, "executeCommand");
|
||||
jest.spyOn(commands, "registerCommand").mockImplementation(() => ({
|
||||
dispose: jest.fn(),
|
||||
}));
|
||||
jest
|
||||
.spyOn(commands, "executeCommand")
|
||||
.mockImplementation(() => Promise.resolve());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
if (viewer) {
|
||||
viewer.dispose(testDisposeHandler);
|
||||
viewer = undefined;
|
||||
@@ -36,9 +35,9 @@ describe("AstViewer", () => {
|
||||
viewer = new AstViewer();
|
||||
viewer.updateRoots(astRoots, item, Uri.file("def/abc"));
|
||||
|
||||
expect((viewer as any).treeDataProvider.roots).to.eq(astRoots);
|
||||
expect((viewer as any).treeDataProvider.db).to.eq(item);
|
||||
expect((viewer as any).treeView.message).to.eq("AST for abc");
|
||||
expect((viewer as any).treeDataProvider.roots).toBe(astRoots);
|
||||
expect((viewer as any).treeDataProvider.db).toBe(item);
|
||||
expect((viewer as any).treeView.message).toBe("AST for abc");
|
||||
});
|
||||
|
||||
it("should update the tree selection based on a change in the editor selection", () => {
|
||||
@@ -49,7 +48,7 @@ describe("AstViewer", () => {
|
||||
it("should select an AssignExpr", () => {
|
||||
// this one is interesting because it spans a couple of other nodes
|
||||
const expr = findNodeById(300, astRoots);
|
||||
expect(expr.label).to.eq("[AssignExpr] ... = ...");
|
||||
expect(expr.label).toBe("[AssignExpr] ... = ...");
|
||||
doSelectionTest(expr, expr.fileLocation?.range);
|
||||
});
|
||||
|
||||
@@ -75,8 +74,10 @@ describe("AstViewer", () => {
|
||||
const item = {} as DatabaseItem;
|
||||
viewer = new AstViewer();
|
||||
viewer.updateRoots(astRoots, item, defaultUri);
|
||||
const spy = sandbox.spy();
|
||||
(viewer as any).treeView.reveal = spy;
|
||||
|
||||
const revealMock = jest.fn();
|
||||
|
||||
(viewer as any).treeView.reveal = revealMock;
|
||||
Object.defineProperty((viewer as any).treeView, "visible", {
|
||||
value: true,
|
||||
});
|
||||
@@ -84,9 +85,9 @@ describe("AstViewer", () => {
|
||||
const mockEvent = createMockEvent(selectionRange, fileUri);
|
||||
(viewer as any).updateTreeSelection(mockEvent);
|
||||
if (expectedSelection) {
|
||||
expect(spy).to.have.been.calledWith(expectedSelection);
|
||||
expect(revealMock).toBeCalledWith(expectedSelection);
|
||||
} else {
|
||||
expect(spy).not.to.have.been.called;
|
||||
expect(revealMock).not.toBeCalled();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import * as fs from "fs-extra";
|
||||
import { expect } from "chai";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import AstBuilder from "../../../contextual/astBuilder";
|
||||
import { CodeQLCliServer } from "../../../cli";
|
||||
@@ -35,9 +33,9 @@ describe("AstBuilder", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
mockCli = {
|
||||
bqrsDecode: sinon
|
||||
.stub()
|
||||
.callsFake(
|
||||
bqrsDecode: jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(_: string, resultSet: "nodes" | "edges" | "graphProperties") => {
|
||||
return mockDecode(resultSet);
|
||||
},
|
||||
@@ -55,23 +53,15 @@ describe("AstBuilder", () => {
|
||||
const roots = await astBuilder.getRoots();
|
||||
|
||||
const options = { entities: ["id", "url", "string"] };
|
||||
expect(mockCli.bqrsDecode).to.have.been.calledWith(
|
||||
"/a/b/c",
|
||||
"nodes",
|
||||
options,
|
||||
);
|
||||
expect(mockCli.bqrsDecode).to.have.been.calledWith(
|
||||
"/a/b/c",
|
||||
"edges",
|
||||
options,
|
||||
);
|
||||
expect(mockCli.bqrsDecode).to.have.been.calledWith(
|
||||
expect(mockCli.bqrsDecode).toBeCalledWith("/a/b/c", "nodes", options);
|
||||
expect(mockCli.bqrsDecode).toBeCalledWith("/a/b/c", "edges", options);
|
||||
expect(mockCli.bqrsDecode).toBeCalledWith(
|
||||
"/a/b/c",
|
||||
"graphProperties",
|
||||
options,
|
||||
);
|
||||
|
||||
expect(roots.map((r) => ({ ...r, children: undefined }))).to.deep.eq(
|
||||
expect(roots.map((r) => ({ ...r, children: undefined }))).toEqual(
|
||||
expectedRoots,
|
||||
);
|
||||
});
|
||||
@@ -82,7 +72,7 @@ describe("AstBuilder", () => {
|
||||
const astBuilder = createAstBuilder();
|
||||
const roots = await astBuilder.getRoots();
|
||||
|
||||
expect(roots[0].children[0].parent).to.eq(roots[0]);
|
||||
expect(roots[0].children[0].parent).toBe(roots[0]);
|
||||
// break the recursion
|
||||
(roots[0].children[0] as any).parent = undefined;
|
||||
(roots[0].children[0] as any).children = undefined;
|
||||
@@ -103,7 +93,7 @@ describe("AstBuilder", () => {
|
||||
parent: undefined,
|
||||
};
|
||||
|
||||
expect(roots[0].children[0]).to.deep.eq(child);
|
||||
expect(roots[0].children[0]).toEqual(child);
|
||||
});
|
||||
|
||||
it("should build an AST child with edge label", async () => {
|
||||
@@ -112,7 +102,7 @@ describe("AstBuilder", () => {
|
||||
const astBuilder = createAstBuilder();
|
||||
const roots = await astBuilder.getRoots();
|
||||
|
||||
expect(roots[0].children[1].parent).to.eq(roots[0]);
|
||||
expect(roots[0].children[1].parent).toBe(roots[0]);
|
||||
// break the recursion
|
||||
(roots[0].children[1] as any).parent = undefined;
|
||||
(roots[0].children[1] as any).children = undefined;
|
||||
@@ -133,7 +123,7 @@ describe("AstBuilder", () => {
|
||||
parent: undefined,
|
||||
};
|
||||
|
||||
expect(roots[0].children[1]).to.deep.eq(child);
|
||||
expect(roots[0].children[1]).toEqual(child);
|
||||
});
|
||||
|
||||
it("should fail when graphProperties are not correct", async () => {
|
||||
@@ -142,7 +132,7 @@ describe("AstBuilder", () => {
|
||||
};
|
||||
|
||||
const astBuilder = createAstBuilder();
|
||||
await expect(astBuilder.getRoots()).to.be.rejectedWith("AST is invalid");
|
||||
await expect(astBuilder.getRoots()).rejects.toThrow("AST is invalid");
|
||||
});
|
||||
|
||||
function createAstBuilder() {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { expect } from "chai";
|
||||
import { Uri, Range } from "vscode";
|
||||
|
||||
import fileRangeFromURI from "../../../contextual/fileRangeFromURI";
|
||||
@@ -10,8 +9,9 @@ import {
|
||||
|
||||
describe("fileRangeFromURI", () => {
|
||||
it("should return undefined when value is not a file URI", () => {
|
||||
expect(fileRangeFromURI("hucairz", createMockDatabaseItem())).to.be
|
||||
.undefined;
|
||||
expect(
|
||||
fileRangeFromURI("hucairz", createMockDatabaseItem()),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should fail to find a location when not a file URI and a full 5 part location", () => {
|
||||
@@ -26,7 +26,7 @@ describe("fileRangeFromURI", () => {
|
||||
} as LineColumnLocation,
|
||||
createMockDatabaseItem(),
|
||||
),
|
||||
).to.be.undefined;
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should fail to find a location when there is a silly protocol", () => {
|
||||
@@ -41,7 +41,7 @@ describe("fileRangeFromURI", () => {
|
||||
} as LineColumnLocation,
|
||||
createMockDatabaseItem(),
|
||||
),
|
||||
).to.be.undefined;
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return undefined when value is an empty uri", () => {
|
||||
@@ -56,7 +56,7 @@ describe("fileRangeFromURI", () => {
|
||||
} as LineColumnLocation,
|
||||
createMockDatabaseItem(),
|
||||
),
|
||||
).to.be.undefined;
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return a range for a WholeFileLocation", () => {
|
||||
@@ -67,7 +67,7 @@ describe("fileRangeFromURI", () => {
|
||||
} as WholeFileLocation,
|
||||
createMockDatabaseItem(),
|
||||
),
|
||||
).to.deep.eq({
|
||||
).toEqual({
|
||||
uri: Uri.parse("file:///hucairz", true),
|
||||
range: new Range(0, 0, 0, 0),
|
||||
});
|
||||
@@ -85,7 +85,7 @@ describe("fileRangeFromURI", () => {
|
||||
} as LineColumnLocation,
|
||||
createMockDatabaseItem(),
|
||||
),
|
||||
).to.deep.eq({
|
||||
).toEqual({
|
||||
uri: Uri.parse("file:///hucairz", true),
|
||||
range: new Range(0, 1, 2, 4),
|
||||
});
|
||||
|
||||
@@ -1,42 +1,63 @@
|
||||
import * as yaml from "js-yaml";
|
||||
import * as sinon from "sinon";
|
||||
import { expect } from "chai";
|
||||
import * as pq from "proxyquire";
|
||||
import * as fs from "fs-extra";
|
||||
|
||||
import { KeyType } from "../../../contextual/keyType";
|
||||
import { getErrorMessage } from "../../../pure/helpers-pure";
|
||||
|
||||
const proxyquire = pq.noPreserveCache().noCallThru();
|
||||
import * as helpers from "../../../helpers";
|
||||
import {
|
||||
qlpackOfDatabase,
|
||||
resolveQueries,
|
||||
} from "../../../contextual/queryResolver";
|
||||
import { CodeQLCliServer } from "../../../cli";
|
||||
import { DatabaseItem } from "../../../databases";
|
||||
|
||||
describe("queryResolver", () => {
|
||||
let module: Record<string, Function>;
|
||||
let writeFileSpy: sinon.SinonSpy;
|
||||
let getQlPackForDbschemeSpy: sinon.SinonStub;
|
||||
let getPrimaryDbschemeSpy: sinon.SinonStub;
|
||||
let mockCli: Record<
|
||||
string,
|
||||
sinon.SinonStub | Record<string, sinon.SinonStub>
|
||||
>;
|
||||
const writeFileSpy = jest.spyOn(fs, "writeFile");
|
||||
|
||||
const getQlPackForDbschemeSpy = jest.spyOn(helpers, "getQlPackForDbscheme");
|
||||
const getPrimaryDbschemeSpy = jest.spyOn(helpers, "getPrimaryDbscheme");
|
||||
jest.spyOn(helpers, "getOnDiskWorkspaceFolders").mockReturnValue([]);
|
||||
jest.spyOn(helpers, "showAndLogErrorMessage").mockResolvedValue(undefined);
|
||||
|
||||
const mockCli = {
|
||||
resolveQueriesInSuite: jest.fn(),
|
||||
cliConstraints: {
|
||||
supportsAllowLibraryPacksInResolveQueries: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockCli = {
|
||||
resolveQueriesInSuite: sinon.stub(),
|
||||
cliConstraints: {
|
||||
supportsAllowLibraryPacksInResolveQueries: sinon.stub().returns(true),
|
||||
},
|
||||
};
|
||||
module = createModule();
|
||||
writeFileSpy.mockReset().mockImplementation(() => Promise.resolve());
|
||||
|
||||
getQlPackForDbschemeSpy.mockReset().mockResolvedValue({
|
||||
dbschemePack: "dbschemePack",
|
||||
dbschemePackIsLibraryPack: false,
|
||||
});
|
||||
getPrimaryDbschemeSpy.mockReset().mockResolvedValue("primaryDbscheme");
|
||||
|
||||
mockCli.resolveQueriesInSuite.mockReset();
|
||||
mockCli.cliConstraints.supportsAllowLibraryPacksInResolveQueries
|
||||
.mockReset()
|
||||
.mockReturnValue(true);
|
||||
});
|
||||
|
||||
describe("resolveQueries", () => {
|
||||
it("should resolve a query", async () => {
|
||||
mockCli.resolveQueriesInSuite.returns(["a", "b"]);
|
||||
const result = await module.resolveQueries(
|
||||
mockCli,
|
||||
{ dbschemePack: "my-qlpack" },
|
||||
mockCli.resolveQueriesInSuite.mockReturnValue(["a", "b"]);
|
||||
const result = await resolveQueries(
|
||||
mockCli as unknown as CodeQLCliServer,
|
||||
{ dbschemePack: "my-qlpack", dbschemePackIsLibraryPack: false },
|
||||
KeyType.DefinitionQuery,
|
||||
);
|
||||
expect(result).to.deep.equal(["a", "b"]);
|
||||
expect(writeFileSpy.getCall(0).args[0]).to.match(/.qls$/);
|
||||
expect(yaml.load(writeFileSpy.getCall(0).args[1])).to.deep.equal([
|
||||
expect(result).toEqual(["a", "b"]);
|
||||
expect(writeFileSpy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.stringMatching(/.qls$/),
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
);
|
||||
expect(yaml.load(writeFileSpy.mock.calls[0][1])).toEqual([
|
||||
{
|
||||
from: "my-qlpack",
|
||||
queries: ".",
|
||||
@@ -50,12 +71,12 @@ describe("queryResolver", () => {
|
||||
|
||||
it("should resolve a query from the queries pack if this is an old CLI", async () => {
|
||||
// pretend this is an older CLI
|
||||
(
|
||||
mockCli.cliConstraints as any
|
||||
).supportsAllowLibraryPacksInResolveQueries.returns(false);
|
||||
mockCli.resolveQueriesInSuite.returns(["a", "b"]);
|
||||
const result = await module.resolveQueries(
|
||||
mockCli,
|
||||
mockCli.cliConstraints.supportsAllowLibraryPacksInResolveQueries.mockReturnValue(
|
||||
false,
|
||||
);
|
||||
mockCli.resolveQueriesInSuite.mockReturnValue(["a", "b"]);
|
||||
const result = await resolveQueries(
|
||||
mockCli as unknown as CodeQLCliServer,
|
||||
{
|
||||
dbschemePackIsLibraryPack: true,
|
||||
dbschemePack: "my-qlpack",
|
||||
@@ -63,9 +84,14 @@ describe("queryResolver", () => {
|
||||
},
|
||||
KeyType.DefinitionQuery,
|
||||
);
|
||||
expect(result).to.deep.equal(["a", "b"]);
|
||||
expect(writeFileSpy.getCall(0).args[0]).to.match(/.qls$/);
|
||||
expect(yaml.load(writeFileSpy.getCall(0).args[1])).to.deep.equal([
|
||||
expect(result).toEqual(["a", "b"]);
|
||||
expect(writeFileSpy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.stringMatching(/.qls$/),
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
);
|
||||
expect(yaml.load(writeFileSpy.mock.calls[0][1])).toEqual([
|
||||
{
|
||||
from: "my-qlpack2",
|
||||
queries: ".",
|
||||
@@ -78,18 +104,18 @@ describe("queryResolver", () => {
|
||||
});
|
||||
|
||||
it("should throw an error when there are no queries found", async () => {
|
||||
mockCli.resolveQueriesInSuite.returns([]);
|
||||
mockCli.resolveQueriesInSuite.mockReturnValue([]);
|
||||
|
||||
try {
|
||||
await module.resolveQueries(
|
||||
mockCli,
|
||||
{ dbschemePack: "my-qlpack" },
|
||||
await resolveQueries(
|
||||
mockCli as unknown as CodeQLCliServer,
|
||||
{ dbschemePack: "my-qlpack", dbschemePackIsLibraryPack: false },
|
||||
KeyType.DefinitionQuery,
|
||||
);
|
||||
// should reject
|
||||
expect(true).to.be.false;
|
||||
expect(true).toBe(false);
|
||||
} catch (e) {
|
||||
expect(getErrorMessage(e)).to.eq(
|
||||
expect(getErrorMessage(e)).toBe(
|
||||
"Couldn't find any queries tagged ide-contextual-queries/local-definitions in any of the following packs: my-qlpack.",
|
||||
);
|
||||
}
|
||||
@@ -98,37 +124,26 @@ describe("queryResolver", () => {
|
||||
|
||||
describe("qlpackOfDatabase", () => {
|
||||
it("should get the qlpack of a database", async () => {
|
||||
getQlPackForDbschemeSpy.resolves("my-qlpack");
|
||||
getQlPackForDbschemeSpy.mockResolvedValue({
|
||||
dbschemePack: "my-qlpack",
|
||||
dbschemePackIsLibraryPack: false,
|
||||
});
|
||||
const db = {
|
||||
contents: {
|
||||
datasetUri: {
|
||||
fsPath: "/path/to/database",
|
||||
},
|
||||
},
|
||||
};
|
||||
const result = await module.qlpackOfDatabase(mockCli, db);
|
||||
expect(result).to.eq("my-qlpack");
|
||||
expect(getPrimaryDbschemeSpy).to.have.been.calledWith(
|
||||
"/path/to/database",
|
||||
} as unknown as DatabaseItem;
|
||||
const result = await qlpackOfDatabase(
|
||||
mockCli as unknown as CodeQLCliServer,
|
||||
db,
|
||||
);
|
||||
expect(result).toEqual({
|
||||
dbschemePack: "my-qlpack",
|
||||
dbschemePackIsLibraryPack: false,
|
||||
});
|
||||
expect(getPrimaryDbschemeSpy).toBeCalledWith("/path/to/database");
|
||||
});
|
||||
});
|
||||
|
||||
function createModule() {
|
||||
writeFileSpy = sinon.spy();
|
||||
getQlPackForDbschemeSpy = sinon.stub();
|
||||
getPrimaryDbschemeSpy = sinon.stub();
|
||||
return proxyquire("../../../contextual/queryResolver", {
|
||||
"fs-extra": {
|
||||
writeFile: writeFileSpy,
|
||||
},
|
||||
|
||||
"../helpers": {
|
||||
getQlPackForDbscheme: getQlPackForDbschemeSpy,
|
||||
getPrimaryDbscheme: getPrimaryDbschemeSpy,
|
||||
getOnDiskWorkspaceFolders: () => ({}),
|
||||
showAndLogErrorMessage: () => ({}),
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import * as sinon from "sinon";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import * as tmp from "tmp";
|
||||
import { expect } from "chai";
|
||||
import { window } from "vscode";
|
||||
import { QuickPickItem, window } from "vscode";
|
||||
|
||||
import {
|
||||
convertGithubNwoToDatabaseUrl,
|
||||
@@ -12,32 +10,24 @@ import {
|
||||
findDirWithFile,
|
||||
looksLikeGithubRepo,
|
||||
} from "../../databaseFetcher";
|
||||
import { ProgressCallback } from "../../commandRunner";
|
||||
import * as Octokit from "@octokit/rest";
|
||||
|
||||
describe("databaseFetcher", function () {
|
||||
// These tests make API calls and may need extra time to complete.
|
||||
this.timeout(10000);
|
||||
// These tests make API calls and may need extra time to complete.
|
||||
jest.setTimeout(10000);
|
||||
|
||||
describe("databaseFetcher", () => {
|
||||
describe("convertGithubNwoToDatabaseUrl", () => {
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let quickPickSpy: sinon.SinonStub;
|
||||
let progressSpy: ProgressCallback;
|
||||
let mockRequest: sinon.SinonStub;
|
||||
let octokit: Octokit.Octokit;
|
||||
const quickPickSpy = jest.spyOn(window, "showQuickPick");
|
||||
const progressSpy = jest.fn();
|
||||
const mockRequest = jest.fn();
|
||||
const octokit: Octokit.Octokit = {
|
||||
request: mockRequest,
|
||||
} as unknown as Octokit.Octokit;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
quickPickSpy = sandbox.stub(window, "showQuickPick");
|
||||
progressSpy = sandbox.spy();
|
||||
mockRequest = sandbox.stub();
|
||||
octokit = {
|
||||
request: mockRequest,
|
||||
} as unknown as Octokit.Octokit;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
quickPickSpy.mockReset().mockResolvedValue(undefined);
|
||||
progressSpy.mockReset();
|
||||
mockRequest.mockReset();
|
||||
});
|
||||
|
||||
it("should convert a GitHub nwo to a database url", async () => {
|
||||
@@ -82,31 +72,31 @@ describe("databaseFetcher", function () {
|
||||
},
|
||||
],
|
||||
};
|
||||
mockRequest.resolves(mockApiResponse);
|
||||
quickPickSpy.resolves("javascript");
|
||||
mockRequest.mockResolvedValue(mockApiResponse);
|
||||
quickPickSpy.mockResolvedValue("javascript" as unknown as QuickPickItem);
|
||||
const githubRepo = "github/codeql";
|
||||
const result = await convertGithubNwoToDatabaseUrl(
|
||||
githubRepo,
|
||||
octokit,
|
||||
progressSpy,
|
||||
);
|
||||
expect(result).not.to.be.undefined;
|
||||
expect(result).toBeDefined();
|
||||
if (result === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { databaseUrl, name, owner } = result;
|
||||
|
||||
expect(databaseUrl).to.equal(
|
||||
expect(databaseUrl).toBe(
|
||||
"https://api.github.com/repos/github/codeql/code-scanning/codeql/databases/javascript",
|
||||
);
|
||||
expect(name).to.equal("codeql");
|
||||
expect(owner).to.equal("github");
|
||||
expect(quickPickSpy.firstCall.args[0]).to.deep.equal([
|
||||
"csharp",
|
||||
"javascript",
|
||||
"ql",
|
||||
]);
|
||||
expect(name).toBe("codeql");
|
||||
expect(owner).toBe("github");
|
||||
expect(quickPickSpy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
["csharp", "javascript", "ql"],
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
// Repository doesn't exist, or the user has no access to the repository.
|
||||
@@ -117,12 +107,12 @@ describe("databaseFetcher", function () {
|
||||
},
|
||||
status: 404,
|
||||
};
|
||||
mockRequest.resolves(mockApiResponse);
|
||||
mockRequest.mockResolvedValue(mockApiResponse);
|
||||
const githubRepo = "foo/bar-not-real";
|
||||
await expect(
|
||||
convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy),
|
||||
).to.be.rejectedWith(/Unable to get database/);
|
||||
expect(progressSpy).to.have.callCount(0);
|
||||
).rejects.toThrow(/Unable to get database/);
|
||||
expect(progressSpy).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
// User has access to the repository, but there are no databases for any language.
|
||||
@@ -131,121 +121,124 @@ describe("databaseFetcher", function () {
|
||||
data: [],
|
||||
};
|
||||
|
||||
mockRequest.resolves(mockApiResponse);
|
||||
mockRequest.mockResolvedValue(mockApiResponse);
|
||||
const githubRepo = "foo/bar-with-no-dbs";
|
||||
await expect(
|
||||
convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy),
|
||||
).to.be.rejectedWith(/Unable to get database/);
|
||||
expect(progressSpy).to.have.been.calledOnce;
|
||||
).rejects.toThrow(/Unable to get database/);
|
||||
expect(progressSpy).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("convertLgtmUrlToDatabaseUrl", () => {
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let quickPickSpy: sinon.SinonStub;
|
||||
let progressSpy: ProgressCallback;
|
||||
const quickPickSpy = jest.spyOn(window, "showQuickPick");
|
||||
const progressSpy = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
quickPickSpy = sandbox.stub(window, "showQuickPick");
|
||||
progressSpy = sandbox.spy();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
quickPickSpy.mockReset().mockResolvedValue(undefined);
|
||||
progressSpy.mockReset();
|
||||
});
|
||||
|
||||
it("should convert a project url to a database url", async () => {
|
||||
quickPickSpy.resolves("javascript");
|
||||
quickPickSpy.mockResolvedValue("javascript" as unknown as QuickPickItem);
|
||||
const lgtmUrl = "https://lgtm.com/projects/g/github/codeql";
|
||||
const dbUrl = await convertLgtmUrlToDatabaseUrl(lgtmUrl, progressSpy);
|
||||
|
||||
expect(dbUrl).to.equal(
|
||||
expect(dbUrl).toBe(
|
||||
"https://lgtm.com/api/v1.0/snapshots/1506465042581/javascript",
|
||||
);
|
||||
expect(quickPickSpy.firstCall.args[0]).to.contain("javascript");
|
||||
expect(quickPickSpy.firstCall.args[0]).to.contain("python");
|
||||
expect(quickPickSpy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.arrayContaining(["javascript", "python"]),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it("should convert a project url to a database url with extra path segments", async () => {
|
||||
quickPickSpy.resolves("python");
|
||||
quickPickSpy.mockResolvedValue("python" as unknown as QuickPickItem);
|
||||
const lgtmUrl =
|
||||
"https://lgtm.com/projects/g/github/codeql/subpage/subpage2?query=xxx";
|
||||
const dbUrl = await convertLgtmUrlToDatabaseUrl(lgtmUrl, progressSpy);
|
||||
|
||||
expect(dbUrl).to.equal(
|
||||
expect(dbUrl).toBe(
|
||||
"https://lgtm.com/api/v1.0/snapshots/1506465042581/python",
|
||||
);
|
||||
expect(progressSpy).to.have.been.calledOnce;
|
||||
expect(progressSpy).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should convert a raw slug to a database url with extra path segments", async () => {
|
||||
quickPickSpy.resolves("python");
|
||||
quickPickSpy.mockResolvedValue("python" as unknown as QuickPickItem);
|
||||
const lgtmUrl = "g/github/codeql";
|
||||
const dbUrl = await convertLgtmUrlToDatabaseUrl(lgtmUrl, progressSpy);
|
||||
|
||||
expect(dbUrl).to.equal(
|
||||
expect(dbUrl).toBe(
|
||||
"https://lgtm.com/api/v1.0/snapshots/1506465042581/python",
|
||||
);
|
||||
expect(progressSpy).to.have.been.calledOnce;
|
||||
expect(progressSpy).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should fail on a nonexistent project", async () => {
|
||||
quickPickSpy.resolves("javascript");
|
||||
quickPickSpy.mockResolvedValue("javascript" as unknown as QuickPickItem);
|
||||
const lgtmUrl = "https://lgtm.com/projects/g/github/hucairz";
|
||||
await expect(
|
||||
convertLgtmUrlToDatabaseUrl(lgtmUrl, progressSpy),
|
||||
).to.rejectedWith(/Invalid LGTM URL/);
|
||||
expect(progressSpy).to.have.callCount(0);
|
||||
).rejects.toThrow(/Invalid LGTM URL/);
|
||||
expect(progressSpy).toBeCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("looksLikeGithubRepo", () => {
|
||||
it("should handle invalid urls", () => {
|
||||
expect(looksLikeGithubRepo("")).to.be.false;
|
||||
expect(looksLikeGithubRepo("http://github.com/foo/bar")).to.be.false;
|
||||
expect(looksLikeGithubRepo("https://ww.github.com/foo/bar")).to.be.false;
|
||||
expect(looksLikeGithubRepo("https://ww.github.com/foo")).to.be.false;
|
||||
expect(looksLikeGithubRepo("foo")).to.be.false;
|
||||
expect(looksLikeGithubRepo("")).toBe(false);
|
||||
expect(looksLikeGithubRepo("http://github.com/foo/bar")).toBe(false);
|
||||
expect(looksLikeGithubRepo("https://ww.github.com/foo/bar")).toBe(false);
|
||||
expect(looksLikeGithubRepo("https://ww.github.com/foo")).toBe(false);
|
||||
expect(looksLikeGithubRepo("foo")).toBe(false);
|
||||
});
|
||||
|
||||
it("should handle valid urls", () => {
|
||||
expect(looksLikeGithubRepo("https://github.com/foo/bar")).to.be.true;
|
||||
expect(looksLikeGithubRepo("https://www.github.com/foo/bar")).to.be.true;
|
||||
expect(looksLikeGithubRepo("https://github.com/foo/bar/sub/pages")).to.be
|
||||
.true;
|
||||
expect(looksLikeGithubRepo("foo/bar")).to.be.true;
|
||||
expect(looksLikeGithubRepo("https://github.com/foo/bar")).toBe(true);
|
||||
expect(looksLikeGithubRepo("https://www.github.com/foo/bar")).toBe(true);
|
||||
expect(looksLikeGithubRepo("https://github.com/foo/bar/sub/pages")).toBe(
|
||||
true,
|
||||
);
|
||||
expect(looksLikeGithubRepo("foo/bar")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("looksLikeLgtmUrl", () => {
|
||||
it("should handle invalid urls", () => {
|
||||
expect(looksLikeLgtmUrl("")).to.be.false;
|
||||
expect(looksLikeLgtmUrl("http://lgtm.com/projects/g/github/codeql")).to.be
|
||||
.false;
|
||||
expect(looksLikeLgtmUrl("https://ww.lgtm.com/projects/g/github/codeql"))
|
||||
.to.be.false;
|
||||
expect(looksLikeLgtmUrl("https://ww.lgtm.com/projects/g/github")).to.be
|
||||
.false;
|
||||
expect(looksLikeLgtmUrl("g/github")).to.be.false;
|
||||
expect(looksLikeLgtmUrl("ggg/github/myproj")).to.be.false;
|
||||
expect(looksLikeLgtmUrl("")).toBe(false);
|
||||
expect(looksLikeLgtmUrl("http://lgtm.com/projects/g/github/codeql")).toBe(
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
looksLikeLgtmUrl("https://ww.lgtm.com/projects/g/github/codeql"),
|
||||
).toBe(false);
|
||||
expect(looksLikeLgtmUrl("https://ww.lgtm.com/projects/g/github")).toBe(
|
||||
false,
|
||||
);
|
||||
expect(looksLikeLgtmUrl("g/github")).toBe(false);
|
||||
expect(looksLikeLgtmUrl("ggg/github/myproj")).toBe(false);
|
||||
});
|
||||
|
||||
it("should handle valid urls", () => {
|
||||
expect(looksLikeLgtmUrl("https://lgtm.com/projects/g/github/codeql")).to
|
||||
.be.true;
|
||||
expect(looksLikeLgtmUrl("https://www.lgtm.com/projects/g/github/codeql"))
|
||||
.to.be.true;
|
||||
expect(
|
||||
looksLikeLgtmUrl("https://lgtm.com/projects/g/github/codeql"),
|
||||
).toBe(true);
|
||||
expect(
|
||||
looksLikeLgtmUrl("https://www.lgtm.com/projects/g/github/codeql"),
|
||||
).toBe(true);
|
||||
expect(
|
||||
looksLikeLgtmUrl("https://lgtm.com/projects/g/github/codeql/sub/pages"),
|
||||
).to.be.true;
|
||||
).toBe(true);
|
||||
expect(
|
||||
looksLikeLgtmUrl(
|
||||
"https://lgtm.com/projects/g/github/codeql/sub/pages?query=string",
|
||||
),
|
||||
).to.be.true;
|
||||
expect(looksLikeLgtmUrl("g/github/myproj")).to.be.true;
|
||||
expect(looksLikeLgtmUrl("git/github/myproj")).to.be.true;
|
||||
).toBe(true);
|
||||
expect(looksLikeLgtmUrl("g/github/myproj")).toBe(true);
|
||||
expect(looksLikeLgtmUrl("git/github/myproj")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -274,21 +267,21 @@ describe("databaseFetcher", function () {
|
||||
});
|
||||
|
||||
it("should find files", async () => {
|
||||
expect(await findDirWithFile(dir.name, "k")).to.equal(
|
||||
expect(await findDirWithFile(dir.name, "k")).toBe(
|
||||
path.join(dir.name, "dir2", "dir3"),
|
||||
);
|
||||
expect(await findDirWithFile(dir.name, "h")).to.equal(
|
||||
expect(await findDirWithFile(dir.name, "h")).toBe(
|
||||
path.join(dir.name, "dir2"),
|
||||
);
|
||||
expect(await findDirWithFile(dir.name, "z", "a")).to.equal(dir.name);
|
||||
expect(await findDirWithFile(dir.name, "z", "a")).toBe(dir.name);
|
||||
// there's some slight indeterminism when more than one name exists
|
||||
// but in general, this will find files in the current directory before
|
||||
// finding files in sub-dirs
|
||||
expect(await findDirWithFile(dir.name, "k", "a")).to.equal(dir.name);
|
||||
expect(await findDirWithFile(dir.name, "k", "a")).toBe(dir.name);
|
||||
});
|
||||
|
||||
it("should not find files", async () => {
|
||||
expect(await findDirWithFile(dir.name, "x", "y", "z")).to.be.undefined;
|
||||
expect(await findDirWithFile(dir.name, "x", "y", "z")).toBeUndefined();
|
||||
});
|
||||
|
||||
function createFile(...segments: string[]) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as tmp from "tmp";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import { expect } from "chai";
|
||||
import { Uri } from "vscode";
|
||||
|
||||
import { DatabaseUI } from "../../databases-ui";
|
||||
@@ -14,13 +13,13 @@ describe("databases-ui", () => {
|
||||
it("should choose current directory direcory normally", async () => {
|
||||
const dir = tmp.dirSync().name;
|
||||
const uri = await fixDbUri(Uri.file(dir));
|
||||
expect(uri.toString()).to.eq(Uri.file(dir).toString());
|
||||
expect(uri.toString()).toBe(Uri.file(dir).toString());
|
||||
});
|
||||
|
||||
it("should choose parent direcory when file is selected", async () => {
|
||||
const file = tmp.fileSync().name;
|
||||
const uri = await fixDbUri(Uri.file(file));
|
||||
expect(uri.toString()).to.eq(Uri.file(path.dirname(file)).toString());
|
||||
expect(uri.toString()).toBe(Uri.file(path.dirname(file)).toString());
|
||||
});
|
||||
|
||||
it("should choose parent direcory when db-* is selected", async () => {
|
||||
@@ -29,7 +28,7 @@ describe("databases-ui", () => {
|
||||
await fs.mkdirs(dbDir);
|
||||
|
||||
const uri = await fixDbUri(Uri.file(dbDir));
|
||||
expect(uri.toString()).to.eq(Uri.file(dir).toString());
|
||||
expect(uri.toString()).toBe(Uri.file(dir).toString());
|
||||
});
|
||||
|
||||
it("should choose parent's parent direcory when file selected is in db-*", async () => {
|
||||
@@ -40,7 +39,7 @@ describe("databases-ui", () => {
|
||||
await fs.createFile(file);
|
||||
|
||||
const uri = await fixDbUri(Uri.file(file));
|
||||
expect(uri.toString()).to.eq(Uri.file(dir).toString());
|
||||
expect(uri.toString()).toBe(Uri.file(dir).toString());
|
||||
});
|
||||
|
||||
it("should handle a parent whose name is db-*", async () => {
|
||||
@@ -53,7 +52,7 @@ describe("databases-ui", () => {
|
||||
fs.createFileSync(file);
|
||||
|
||||
const uri = await fixDbUri(Uri.file(file));
|
||||
expect(uri.toString()).to.eq(Uri.file(parentDir).toString());
|
||||
expect(uri.toString()).toBe(Uri.file(parentDir).toString());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,12 +94,12 @@ describe("databases-ui", () => {
|
||||
|
||||
await databaseUI.handleRemoveOrphanedDatabases();
|
||||
|
||||
expect(fs.pathExistsSync(db1)).to.be.true;
|
||||
expect(fs.pathExistsSync(db2)).to.be.true;
|
||||
expect(fs.pathExistsSync(db3)).to.be.true;
|
||||
expect(fs.pathExistsSync(db1)).toBe(true);
|
||||
expect(fs.pathExistsSync(db2)).toBe(true);
|
||||
expect(fs.pathExistsSync(db3)).toBe(true);
|
||||
|
||||
expect(fs.pathExistsSync(db4)).to.be.false;
|
||||
expect(fs.pathExistsSync(db5)).to.be.false;
|
||||
expect(fs.pathExistsSync(db4)).toBe(false);
|
||||
expect(fs.pathExistsSync(db5)).toBe(false);
|
||||
|
||||
databaseUI.dispose(testDisposeHandler);
|
||||
});
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { expect } from "chai";
|
||||
import * as path from "path";
|
||||
import * as fetch from "node-fetch";
|
||||
import * as semver from "semver";
|
||||
import * as sinon from "sinon";
|
||||
import * as pq from "proxyquire";
|
||||
|
||||
import * as helpers from "../../helpers";
|
||||
import { logger } from "../../logging";
|
||||
import * as fs from "fs-extra";
|
||||
import * as os from "os";
|
||||
import {
|
||||
GithubRelease,
|
||||
GithubReleaseAsset,
|
||||
ReleasesApiConsumer,
|
||||
getExecutableFromDirectory,
|
||||
DistributionManager,
|
||||
} from "../../distribution";
|
||||
|
||||
const proxyquire = pq.noPreserveCache();
|
||||
|
||||
describe("Releases API consumer", () => {
|
||||
const owner = "someowner";
|
||||
const repo = "somerepo";
|
||||
@@ -87,7 +88,7 @@ describe("Releases API consumer", () => {
|
||||
const latestRelease = await consumer.getLatestRelease(
|
||||
unconstrainedVersionRange,
|
||||
);
|
||||
expect(latestRelease.id).to.equal(2);
|
||||
expect(latestRelease.id).toBe(2);
|
||||
});
|
||||
|
||||
it("version of picked release is within the version range", async () => {
|
||||
@@ -96,7 +97,7 @@ describe("Releases API consumer", () => {
|
||||
const latestRelease = await consumer.getLatestRelease(
|
||||
new semver.Range("2.*.*"),
|
||||
);
|
||||
expect(latestRelease.id).to.equal(1);
|
||||
expect(latestRelease.id).toBe(1);
|
||||
});
|
||||
|
||||
it("fails if none of the releases are within the version range", async () => {
|
||||
@@ -104,7 +105,7 @@ describe("Releases API consumer", () => {
|
||||
|
||||
await expect(
|
||||
consumer.getLatestRelease(new semver.Range("5.*.*")),
|
||||
).to.be.rejectedWith(Error);
|
||||
).rejects.toThrowError();
|
||||
});
|
||||
|
||||
it("picked release passes additional compatibility test if an additional compatibility test is specified", async () => {
|
||||
@@ -116,7 +117,7 @@ describe("Releases API consumer", () => {
|
||||
(release) =>
|
||||
release.assets.some((asset) => asset.name === "exampleAsset.txt"),
|
||||
);
|
||||
expect(latestRelease.id).to.equal(3);
|
||||
expect(latestRelease.id).toBe(3);
|
||||
});
|
||||
|
||||
it("fails if none of the releases pass the additional compatibility test", async () => {
|
||||
@@ -128,7 +129,7 @@ describe("Releases API consumer", () => {
|
||||
(asset) => asset.name === "otherExampleAsset.txt",
|
||||
),
|
||||
),
|
||||
).to.be.rejectedWith(Error);
|
||||
).rejects.toThrowError();
|
||||
});
|
||||
|
||||
it("picked release is the most recent prerelease when includePrereleases is set", async () => {
|
||||
@@ -138,7 +139,7 @@ describe("Releases API consumer", () => {
|
||||
unconstrainedVersionRange,
|
||||
true,
|
||||
);
|
||||
expect(latestRelease.id).to.equal(5);
|
||||
expect(latestRelease.id).toBe(5);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -183,11 +184,11 @@ describe("Releases API consumer", () => {
|
||||
const assets = (await consumer.getLatestRelease(unconstrainedVersionRange))
|
||||
.assets;
|
||||
|
||||
expect(assets.length).to.equal(expectedAssets.length);
|
||||
expect(assets.length).toBe(expectedAssets.length);
|
||||
expectedAssets.map((expectedAsset, index) => {
|
||||
expect(assets[index].id).to.equal(expectedAsset.id);
|
||||
expect(assets[index].name).to.equal(expectedAsset.name);
|
||||
expect(assets[index].size).to.equal(expectedAsset.size);
|
||||
expect(assets[index].id).toBe(expectedAsset.id);
|
||||
expect(assets[index].name).toBe(expectedAsset.name);
|
||||
expect(assets[index].size).toBe(expectedAsset.size);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -196,82 +197,80 @@ describe("Launcher path", () => {
|
||||
const pathToCmd = `abc${path.sep}codeql.cmd`;
|
||||
const pathToExe = `abc${path.sep}codeql.exe`;
|
||||
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let warnSpy: sinon.SinonSpy;
|
||||
let errorSpy: sinon.SinonSpy;
|
||||
let logSpy: sinon.SinonSpy;
|
||||
let fsSpy: sinon.SinonSpy;
|
||||
let platformSpy: sinon.SinonSpy;
|
||||
|
||||
let getExecutableFromDirectory: Function;
|
||||
const warnSpy = jest.spyOn(helpers, "showAndLogWarningMessage");
|
||||
const errorSpy = jest.spyOn(helpers, "showAndLogErrorMessage");
|
||||
const logSpy = jest.spyOn(logger, "log");
|
||||
const pathExistsSpy = jest.spyOn(fs, "pathExists");
|
||||
const platformSpy = jest.spyOn(os, "platform");
|
||||
|
||||
let launcherThatExists = "";
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
getExecutableFromDirectory = createModule().getExecutableFromDirectory;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
warnSpy.mockClear().mockResolvedValue(undefined);
|
||||
errorSpy.mockClear().mockResolvedValue(undefined);
|
||||
logSpy.mockClear().mockResolvedValue(undefined);
|
||||
pathExistsSpy.mockClear().mockImplementation(async (path: string) => {
|
||||
return path.endsWith(launcherThatExists);
|
||||
});
|
||||
platformSpy.mockClear().mockReturnValue("win32");
|
||||
});
|
||||
|
||||
it("should not warn with proper launcher name", async () => {
|
||||
launcherThatExists = "codeql.exe";
|
||||
const result = await getExecutableFromDirectory("abc");
|
||||
expect(fsSpy).to.have.been.calledWith(pathToExe);
|
||||
expect(pathExistsSpy).toBeCalledWith(pathToExe);
|
||||
|
||||
// correct launcher has been found, so alternate one not looked for
|
||||
expect(fsSpy).not.to.have.been.calledWith(pathToCmd);
|
||||
expect(pathExistsSpy).not.toBeCalledWith(pathToCmd);
|
||||
|
||||
// no warning message
|
||||
expect(warnSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
expect(warnSpy).not.toHaveBeenCalled();
|
||||
// No log message
|
||||
expect(logSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
expect(result).to.equal(pathToExe);
|
||||
expect(logSpy).not.toHaveBeenCalled();
|
||||
expect(result).toBe(pathToExe);
|
||||
});
|
||||
|
||||
it("should warn when using a hard-coded deprecated launcher name", async () => {
|
||||
launcherThatExists = "codeql.cmd";
|
||||
const result = await getExecutableFromDirectory("abc");
|
||||
expect(fsSpy).to.have.been.calledWith(pathToExe);
|
||||
expect(fsSpy).to.have.been.calledWith(pathToCmd);
|
||||
expect(pathExistsSpy).toBeCalledWith(pathToExe);
|
||||
expect(pathExistsSpy).toBeCalledWith(pathToCmd);
|
||||
|
||||
// Should have opened a warning message
|
||||
expect(warnSpy).to.have.been.calledWith(sinon.match.string);
|
||||
expect(warnSpy).toHaveBeenCalled();
|
||||
// No log message
|
||||
expect(logSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
expect(result).to.equal(pathToCmd);
|
||||
expect(logSpy).not.toHaveBeenCalled();
|
||||
expect(result).toBe(pathToCmd);
|
||||
});
|
||||
|
||||
it("should avoid warn when no launcher is found", async () => {
|
||||
launcherThatExists = "xxx";
|
||||
const result = await getExecutableFromDirectory("abc", false);
|
||||
expect(fsSpy).to.have.been.calledWith(pathToExe);
|
||||
expect(fsSpy).to.have.been.calledWith(pathToCmd);
|
||||
expect(pathExistsSpy).toBeCalledWith(pathToExe);
|
||||
expect(pathExistsSpy).toBeCalledWith(pathToCmd);
|
||||
|
||||
// no warning message
|
||||
expect(warnSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
expect(warnSpy).not.toHaveBeenCalled();
|
||||
// log message sent out
|
||||
expect(logSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
expect(result).to.equal(undefined);
|
||||
expect(logSpy).not.toHaveBeenCalled();
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should warn when no launcher is found", async () => {
|
||||
launcherThatExists = "xxx";
|
||||
const result = await getExecutableFromDirectory("abc", true);
|
||||
expect(fsSpy).to.have.been.calledWith(pathToExe);
|
||||
expect(fsSpy).to.have.been.calledWith(pathToCmd);
|
||||
expect(pathExistsSpy).toBeCalledWith(pathToExe);
|
||||
expect(pathExistsSpy).toBeCalledWith(pathToCmd);
|
||||
|
||||
// no warning message
|
||||
expect(warnSpy).not.to.have.been.calledWith(sinon.match.string);
|
||||
expect(warnSpy).not.toHaveBeenCalled();
|
||||
// log message sent out
|
||||
expect(logSpy).to.have.been.calledWith(sinon.match.string);
|
||||
expect(result).to.equal(undefined);
|
||||
expect(logSpy).toHaveBeenCalled();
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not warn when deprecated launcher is used, but no new launcher is available", async function () {
|
||||
const manager = new (createModule().DistributionManager)(
|
||||
const manager = new DistributionManager(
|
||||
{ customCodeQlPath: pathToCmd } as any,
|
||||
{} as any,
|
||||
undefined as any,
|
||||
@@ -279,15 +278,15 @@ describe("Launcher path", () => {
|
||||
launcherThatExists = "codeql.cmd";
|
||||
|
||||
const result = await manager.getCodeQlPathWithoutVersionCheck();
|
||||
expect(result).to.equal(pathToCmd);
|
||||
expect(result).toBe(pathToCmd);
|
||||
|
||||
// no warning or error message
|
||||
expect(warnSpy).to.have.callCount(0);
|
||||
expect(errorSpy).to.have.callCount(0);
|
||||
expect(warnSpy).toBeCalledTimes(0);
|
||||
expect(errorSpy).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it("should warn when deprecated launcher is used, and new launcher is available", async () => {
|
||||
const manager = new (createModule().DistributionManager)(
|
||||
const manager = new DistributionManager(
|
||||
{ customCodeQlPath: pathToCmd } as any,
|
||||
{} as any,
|
||||
undefined as any,
|
||||
@@ -295,15 +294,15 @@ describe("Launcher path", () => {
|
||||
launcherThatExists = ""; // pretend both launchers exist
|
||||
|
||||
const result = await manager.getCodeQlPathWithoutVersionCheck();
|
||||
expect(result).to.equal(pathToCmd);
|
||||
expect(result).toBe(pathToCmd);
|
||||
|
||||
// has warning message
|
||||
expect(warnSpy).to.have.callCount(1);
|
||||
expect(errorSpy).to.have.callCount(0);
|
||||
expect(warnSpy).toBeCalledTimes(1);
|
||||
expect(errorSpy).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it("should warn when launcher path is incorrect", async () => {
|
||||
const manager = new (createModule().DistributionManager)(
|
||||
const manager = new DistributionManager(
|
||||
{ customCodeQlPath: pathToCmd } as any,
|
||||
{} as any,
|
||||
undefined as any,
|
||||
@@ -311,39 +310,10 @@ describe("Launcher path", () => {
|
||||
launcherThatExists = "xxx"; // pretend neither launcher exists
|
||||
|
||||
const result = await manager.getCodeQlPathWithoutVersionCheck();
|
||||
expect(result).to.equal(undefined);
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
// no error message
|
||||
expect(warnSpy).to.have.callCount(0);
|
||||
expect(errorSpy).to.have.callCount(1);
|
||||
expect(warnSpy).toBeCalledTimes(0);
|
||||
expect(errorSpy).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
function createModule() {
|
||||
warnSpy = sandbox.spy();
|
||||
errorSpy = sandbox.spy();
|
||||
logSpy = sandbox.spy();
|
||||
// pretend that only the .cmd file exists
|
||||
fsSpy = sandbox
|
||||
.stub()
|
||||
.callsFake((arg) => (arg.endsWith(launcherThatExists) ? true : false));
|
||||
platformSpy = sandbox.stub().returns("win32");
|
||||
|
||||
return proxyquire("../../distribution", {
|
||||
"./helpers": {
|
||||
showAndLogWarningMessage: warnSpy,
|
||||
showAndLogErrorMessage: errorSpy,
|
||||
},
|
||||
"./logging": {
|
||||
logger: {
|
||||
log: logSpy,
|
||||
},
|
||||
},
|
||||
"fs-extra": {
|
||||
pathExists: fsSpy,
|
||||
},
|
||||
os: {
|
||||
platform: platformSpy,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { expect } from "chai";
|
||||
import "mocha";
|
||||
import * as path from "path";
|
||||
|
||||
import {
|
||||
@@ -19,7 +17,7 @@ describe("createDownloadPath", () => {
|
||||
|
||||
const actualPath = createDownloadPath("storage", downloadLink);
|
||||
|
||||
expect(actualPath).to.equal(expectedPath);
|
||||
expect(actualPath).toBe(expectedPath);
|
||||
});
|
||||
|
||||
it("should return the correct path with extension", () => {
|
||||
@@ -34,6 +32,6 @@ describe("createDownloadPath", () => {
|
||||
|
||||
const actualPath = createDownloadPath("storage", downloadLink, "zip");
|
||||
|
||||
expect(actualPath).to.equal(expectedPath);
|
||||
expect(actualPath).toBe(expectedPath);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { expect } from "chai";
|
||||
import EvalLogTreeBuilder from "../../eval-log-tree-builder";
|
||||
import { EvalLogData } from "../../pure/log-summary-parser";
|
||||
|
||||
@@ -60,7 +59,7 @@ describe("EvalLogTreeBuilder", () => {
|
||||
const roots = await builder.getRoots();
|
||||
|
||||
// Force children, parent to be undefined for ease of testing.
|
||||
expect(roots.map((r) => ({ ...r, children: undefined }))).to.deep.eq(
|
||||
expect(roots.map((r) => ({ ...r, children: undefined }))).toEqual(
|
||||
expectedRoots,
|
||||
);
|
||||
|
||||
@@ -70,7 +69,7 @@ describe("EvalLogTreeBuilder", () => {
|
||||
children: undefined,
|
||||
parent: undefined,
|
||||
})),
|
||||
).to.deep.eq(expectedPredicate);
|
||||
).toEqual(expectedPredicate);
|
||||
|
||||
expect(
|
||||
roots[0].children[0].children.map((ra) => ({
|
||||
@@ -78,7 +77,7 @@ describe("EvalLogTreeBuilder", () => {
|
||||
children: undefined,
|
||||
parent: undefined,
|
||||
})),
|
||||
).to.deep.eq(expectedRA);
|
||||
).toEqual(expectedRA);
|
||||
|
||||
// Pipeline steps' children should be empty so do not force undefined children here.
|
||||
expect(
|
||||
@@ -86,7 +85,7 @@ describe("EvalLogTreeBuilder", () => {
|
||||
...step,
|
||||
parent: undefined,
|
||||
})),
|
||||
).to.deep.eq(expectedPipelineSteps);
|
||||
).toEqual(expectedPipelineSteps);
|
||||
});
|
||||
|
||||
it("should build the tree with descriptive message when no data exists", async () => {
|
||||
@@ -107,12 +106,12 @@ describe("EvalLogTreeBuilder", () => {
|
||||
const builder = new EvalLogTreeBuilder("test-query-cached.ql", []);
|
||||
const roots = await builder.getRoots();
|
||||
|
||||
expect(roots.map((r) => ({ ...r, children: undefined }))).to.deep.eq(
|
||||
expect(roots.map((r) => ({ ...r, children: undefined }))).toEqual(
|
||||
expectedRoots,
|
||||
);
|
||||
|
||||
expect(
|
||||
roots[0].children.map((noPreds) => ({ ...noPreds, parent: undefined })),
|
||||
).to.deep.eq(expectedNoPredicates);
|
||||
).toEqual(expectedNoPredicates);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { expect } from "chai";
|
||||
import sinon = require("sinon");
|
||||
import { commands } from "vscode";
|
||||
import {
|
||||
ChildEvalLogTreeItem,
|
||||
@@ -11,18 +9,18 @@ import { testDisposeHandler } from "../test-dispose-handler";
|
||||
describe("EvalLogViewer", () => {
|
||||
let roots: EvalLogTreeItem[];
|
||||
let viewer: EvalLogViewer;
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
beforeEach(async () => {
|
||||
sandbox = sinon.createSandbox();
|
||||
|
||||
viewer = new EvalLogViewer();
|
||||
|
||||
sandbox.stub(commands, "registerCommand");
|
||||
sandbox.stub(commands, "executeCommand");
|
||||
jest.spyOn(commands, "registerCommand").mockImplementation(() => ({
|
||||
dispose: jest.fn(),
|
||||
}));
|
||||
jest
|
||||
.spyOn(commands, "executeCommand")
|
||||
.mockImplementation(() => Promise.resolve());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
if (viewer) {
|
||||
viewer.dispose(testDisposeHandler);
|
||||
}
|
||||
@@ -71,12 +69,12 @@ describe("EvalLogViewer", () => {
|
||||
|
||||
viewer.updateRoots(roots);
|
||||
|
||||
expect((viewer as any).treeDataProvider.roots).to.eq(roots);
|
||||
expect((viewer as any).treeView.message).to.eq("Viewer for query run:");
|
||||
expect((viewer as any).treeDataProvider.roots).toBe(roots);
|
||||
expect((viewer as any).treeView.message).toBe("Viewer for query run:");
|
||||
});
|
||||
|
||||
it("should clear the viewer's roots", () => {
|
||||
viewer.dispose(testDisposeHandler);
|
||||
expect((viewer as any).treeDataProvider.roots.length).to.eq(0);
|
||||
expect((viewer as any).treeDataProvider.roots.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { expect } from "chai";
|
||||
import {
|
||||
EnvironmentVariableCollection,
|
||||
EnvironmentVariableMutator,
|
||||
@@ -15,7 +14,6 @@ import * as yaml from "js-yaml";
|
||||
import * as tmp from "tmp";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import * as sinon from "sinon";
|
||||
import { DirResult } from "tmp";
|
||||
|
||||
import {
|
||||
@@ -29,20 +27,8 @@ import {
|
||||
walkDirectory,
|
||||
} from "../../helpers";
|
||||
import { reportStreamProgress } from "../../commandRunner";
|
||||
import Sinon = require("sinon");
|
||||
import { fail } from "assert";
|
||||
|
||||
describe("helpers", () => {
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("Invocation rate limiter", () => {
|
||||
// 1 January 2020
|
||||
let currentUnixTime = 1577836800;
|
||||
@@ -76,7 +62,7 @@ describe("helpers", () => {
|
||||
},
|
||||
);
|
||||
await invocationRateLimiter.invokeFunctionIfIntervalElapsed(100);
|
||||
expect(numTimesFuncCalled).to.equal(1);
|
||||
expect(numTimesFuncCalled).toBe(1);
|
||||
});
|
||||
|
||||
it("doesn't invoke function again if no time has passed", async () => {
|
||||
@@ -89,7 +75,7 @@ describe("helpers", () => {
|
||||
);
|
||||
await invocationRateLimiter.invokeFunctionIfIntervalElapsed(100);
|
||||
await invocationRateLimiter.invokeFunctionIfIntervalElapsed(100);
|
||||
expect(numTimesFuncCalled).to.equal(1);
|
||||
expect(numTimesFuncCalled).toBe(1);
|
||||
});
|
||||
|
||||
it("doesn't invoke function again if requested time since last invocation hasn't passed", async () => {
|
||||
@@ -103,7 +89,7 @@ describe("helpers", () => {
|
||||
await invocationRateLimiter.invokeFunctionIfIntervalElapsed(100);
|
||||
currentUnixTime += 1;
|
||||
await invocationRateLimiter.invokeFunctionIfIntervalElapsed(2);
|
||||
expect(numTimesFuncCalled).to.equal(1);
|
||||
expect(numTimesFuncCalled).toBe(1);
|
||||
});
|
||||
|
||||
it("invokes function again immediately if requested time since last invocation is 0 seconds", async () => {
|
||||
@@ -116,7 +102,7 @@ describe("helpers", () => {
|
||||
);
|
||||
await invocationRateLimiter.invokeFunctionIfIntervalElapsed(0);
|
||||
await invocationRateLimiter.invokeFunctionIfIntervalElapsed(0);
|
||||
expect(numTimesFuncCalled).to.equal(2);
|
||||
expect(numTimesFuncCalled).toBe(2);
|
||||
});
|
||||
|
||||
it("invokes function again after requested time since last invocation has elapsed", async () => {
|
||||
@@ -130,7 +116,7 @@ describe("helpers", () => {
|
||||
await invocationRateLimiter.invokeFunctionIfIntervalElapsed(1);
|
||||
currentUnixTime += 1;
|
||||
await invocationRateLimiter.invokeFunctionIfIntervalElapsed(1);
|
||||
expect(numTimesFuncCalled).to.equal(2);
|
||||
expect(numTimesFuncCalled).toBe(2);
|
||||
});
|
||||
|
||||
it("invokes functions with different rate limiters", async () => {
|
||||
@@ -150,8 +136,8 @@ describe("helpers", () => {
|
||||
);
|
||||
await invocationRateLimiterA.invokeFunctionIfIntervalElapsed(100);
|
||||
await invocationRateLimiterB.invokeFunctionIfIntervalElapsed(100);
|
||||
expect(numTimesFuncACalled).to.equal(1);
|
||||
expect(numTimesFuncBCalled).to.equal(1);
|
||||
expect(numTimesFuncACalled).toBe(1);
|
||||
expect(numTimesFuncBCalled).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -174,19 +160,19 @@ describe("helpers", () => {
|
||||
});
|
||||
|
||||
it("should get initial query contents when language is known", () => {
|
||||
expect(getInitialQueryContents("cpp", "hucairz")).to.eq(
|
||||
expect(getInitialQueryContents("cpp", "hucairz")).toBe(
|
||||
'import cpp\n\nselect ""',
|
||||
);
|
||||
});
|
||||
|
||||
it("should get initial query contents when dbscheme is known", () => {
|
||||
expect(getInitialQueryContents("", "semmlecode.cpp.dbscheme")).to.eq(
|
||||
expect(getInitialQueryContents("", "semmlecode.cpp.dbscheme")).toBe(
|
||||
'import cpp\n\nselect ""',
|
||||
);
|
||||
});
|
||||
|
||||
it("should get initial query contents when nothing is known", () => {
|
||||
expect(getInitialQueryContents("", "hucairz")).to.eq('select ""');
|
||||
expect(getInitialQueryContents("", "hucairz")).toBe('select ""');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -206,7 +192,7 @@ describe("helpers", () => {
|
||||
fs.mkdirSync(path.join(dbFolder, "db-python"));
|
||||
fs.writeFileSync(path.join(dbFolder, "codeql-database.yml"), "", "utf8");
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).to.be.true;
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(true);
|
||||
});
|
||||
|
||||
it("should likely be a database root: .dbinfo", async () => {
|
||||
@@ -215,7 +201,7 @@ describe("helpers", () => {
|
||||
fs.mkdirSync(path.join(dbFolder, "db-python"));
|
||||
fs.writeFileSync(path.join(dbFolder, ".dbinfo"), "", "utf8");
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).to.be.true;
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(true);
|
||||
});
|
||||
|
||||
it("should likely NOT be a database root: empty dir", async () => {
|
||||
@@ -223,7 +209,7 @@ describe("helpers", () => {
|
||||
fs.mkdirSync(dbFolder);
|
||||
fs.mkdirSync(path.join(dbFolder, "db-python"));
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).to.be.false;
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(false);
|
||||
});
|
||||
|
||||
it("should likely NOT be a database root: no db language folder", async () => {
|
||||
@@ -231,7 +217,7 @@ describe("helpers", () => {
|
||||
fs.mkdirSync(dbFolder);
|
||||
fs.writeFileSync(path.join(dbFolder, ".dbinfo"), "", "utf8");
|
||||
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).to.be.false;
|
||||
expect(await isLikelyDatabaseRoot(dbFolder)).toBe(false);
|
||||
});
|
||||
|
||||
it("should find likely db language folder", async () => {
|
||||
@@ -241,10 +227,10 @@ describe("helpers", () => {
|
||||
fs.writeFileSync(path.join(dbFolder, "codeql-database.yml"), "", "utf8");
|
||||
|
||||
// not a db folder since there is a db-python folder inside this one
|
||||
expect(await isLikelyDbLanguageFolder(dbFolder)).to.be.false;
|
||||
expect(await isLikelyDbLanguageFolder(dbFolder)).toBe(false);
|
||||
|
||||
const nestedDbPythonFolder = path.join(dbFolder, "db-python");
|
||||
expect(await isLikelyDbLanguageFolder(nestedDbPythonFolder)).to.be.true;
|
||||
expect(await isLikelyDbLanguageFolder(nestedDbPythonFolder)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -352,32 +338,33 @@ describe("helpers", () => {
|
||||
}
|
||||
|
||||
it("should report stream progress", () => {
|
||||
const spy = sandbox.spy();
|
||||
const progressSpy = jest.fn();
|
||||
const mockReadable = {
|
||||
on: sandbox.spy(),
|
||||
on: jest.fn(),
|
||||
};
|
||||
const max = 1024 * 1024 * 4;
|
||||
const firstStep = 1024 * 1024 + 1024 * 600;
|
||||
const secondStep = 1024 * 1024 * 2;
|
||||
|
||||
(reportStreamProgress as any)(mockReadable, "My prefix", max, spy);
|
||||
(reportStreamProgress as any)(mockReadable, "My prefix", max, progressSpy);
|
||||
|
||||
// now pretend that we have received some messages
|
||||
mockReadable.on.getCall(0).args[1]({ length: firstStep });
|
||||
mockReadable.on.getCall(0).args[1]({ length: secondStep });
|
||||
const listener = mockReadable.on.mock.calls[0][1] as (data: any) => void;
|
||||
listener({ length: firstStep });
|
||||
listener({ length: secondStep });
|
||||
|
||||
expect(spy).to.have.callCount(3);
|
||||
expect(spy).to.have.been.calledWith({
|
||||
expect(progressSpy).toBeCalledTimes(3);
|
||||
expect(progressSpy).toBeCalledWith({
|
||||
step: 0,
|
||||
maxStep: max,
|
||||
message: "My prefix [0.0 MB of 4.0 MB]",
|
||||
});
|
||||
expect(spy).to.have.been.calledWith({
|
||||
expect(progressSpy).toBeCalledWith({
|
||||
step: firstStep,
|
||||
maxStep: max,
|
||||
message: "My prefix [1.6 MB of 4.0 MB]",
|
||||
});
|
||||
expect(spy).to.have.been.calledWith({
|
||||
expect(progressSpy).toBeCalledWith({
|
||||
step: firstStep + secondStep,
|
||||
maxStep: max,
|
||||
message: "My prefix [3.6 MB of 4.0 MB]",
|
||||
@@ -385,17 +372,22 @@ describe("helpers", () => {
|
||||
});
|
||||
|
||||
it("should report stream progress when total bytes unknown", () => {
|
||||
const spy = sandbox.spy();
|
||||
const progressSpy = jest.fn();
|
||||
const mockReadable = {
|
||||
on: sandbox.spy(),
|
||||
on: jest.fn(),
|
||||
};
|
||||
(reportStreamProgress as any)(mockReadable, "My prefix", undefined, spy);
|
||||
(reportStreamProgress as any)(
|
||||
mockReadable,
|
||||
"My prefix",
|
||||
undefined,
|
||||
progressSpy,
|
||||
);
|
||||
|
||||
// There are no listeners registered to this readable
|
||||
expect(mockReadable.on).not.to.have.been.called;
|
||||
expect(mockReadable.on).not.toBeCalled();
|
||||
|
||||
expect(spy).to.have.callCount(1);
|
||||
expect(spy).to.have.been.calledWith({
|
||||
expect(progressSpy).toBeCalledTimes(1);
|
||||
expect(progressSpy).toBeCalledWith({
|
||||
step: 1,
|
||||
maxStep: 2,
|
||||
message: "My prefix (Size unknown)",
|
||||
@@ -403,106 +395,80 @@ describe("helpers", () => {
|
||||
});
|
||||
|
||||
describe("open dialog", () => {
|
||||
let showInformationMessageSpy: Sinon.SinonStub;
|
||||
const showInformationMessageSpy = jest.spyOn(
|
||||
window,
|
||||
"showInformationMessage",
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
showInformationMessageSpy = sandbox.stub(
|
||||
window,
|
||||
"showInformationMessage",
|
||||
);
|
||||
showInformationMessageSpy.mockClear().mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it("should show a binary choice dialog and return `yes`", (done) => {
|
||||
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.onCall(0).resolvesArg(2);
|
||||
const res = showBinaryChoiceDialog("xxx");
|
||||
res
|
||||
.then((val) => {
|
||||
expect(val).to.eq(true);
|
||||
done();
|
||||
})
|
||||
.catch((e) => fail(e));
|
||||
showInformationMessageSpy.mockImplementationOnce(resolveArg(2));
|
||||
const val = await showBinaryChoiceDialog("xxx");
|
||||
expect(val).toBe(true);
|
||||
});
|
||||
|
||||
it("should show a binary choice dialog and return `no`", (done) => {
|
||||
it("should show a binary choice dialog and return `no`", async () => {
|
||||
// pretend user chooses 'no'
|
||||
showInformationMessageSpy.onCall(0).resolvesArg(3);
|
||||
const res = showBinaryChoiceDialog("xxx");
|
||||
res
|
||||
.then((val) => {
|
||||
expect(val).to.eq(false);
|
||||
done();
|
||||
})
|
||||
.catch((e) => fail(e));
|
||||
showInformationMessageSpy.mockImplementationOnce(resolveArg(3));
|
||||
const val = await showBinaryChoiceDialog("xxx");
|
||||
expect(val).toBe(false);
|
||||
});
|
||||
|
||||
it("should show an info dialog and confirm the action", (done) => {
|
||||
it("should show an info dialog and confirm the action", async () => {
|
||||
// pretend user chooses to run action
|
||||
showInformationMessageSpy.onCall(0).resolvesArg(1);
|
||||
const res = showInformationMessageWithAction("xxx", "yyy");
|
||||
res
|
||||
.then((val) => {
|
||||
expect(val).to.eq(true);
|
||||
done();
|
||||
})
|
||||
.catch((e) => fail(e));
|
||||
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", (done) => {
|
||||
it("should show an action dialog and avoid choosing the action", async () => {
|
||||
// pretend user does not choose to run action
|
||||
showInformationMessageSpy.onCall(0).resolves(undefined);
|
||||
const res = showInformationMessageWithAction("xxx", "yyy");
|
||||
res
|
||||
.then((val) => {
|
||||
expect(val).to.eq(false);
|
||||
done();
|
||||
})
|
||||
.catch((e) => fail(e));
|
||||
showInformationMessageSpy.mockResolvedValueOnce(undefined);
|
||||
const val = await showInformationMessageWithAction("xxx", "yyy");
|
||||
expect(val).toBe(false);
|
||||
});
|
||||
|
||||
it("should show a binary choice dialog with a url and return `yes`", (done) => {
|
||||
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.onCall(0).resolvesArg(2);
|
||||
showInformationMessageSpy.onCall(1).resolvesArg(2);
|
||||
showInformationMessageSpy.onCall(2).resolvesArg(3);
|
||||
const res = showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
|
||||
res
|
||||
.then((val) => {
|
||||
expect(val).to.eq(true);
|
||||
done();
|
||||
})
|
||||
.catch((e) => fail(e));
|
||||
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`", (done) => {
|
||||
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.onCall(0).resolvesArg(2);
|
||||
showInformationMessageSpy.onCall(1).resolvesArg(2);
|
||||
showInformationMessageSpy.onCall(2).resolvesArg(4);
|
||||
const res = showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
|
||||
res
|
||||
.then((val) => {
|
||||
expect(val).to.eq(false);
|
||||
done();
|
||||
})
|
||||
.catch((e) => fail(e));
|
||||
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", (done) => {
|
||||
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.onCall(0).resolvesArg(2);
|
||||
showInformationMessageSpy.onCall(1).resolvesArg(2);
|
||||
showInformationMessageSpy.onCall(2).resolvesArg(2);
|
||||
showInformationMessageSpy.onCall(3).resolvesArg(2);
|
||||
showInformationMessageSpy.onCall(4).resolvesArg(2);
|
||||
const res = showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
|
||||
res
|
||||
.then((val) => {
|
||||
// No choie was made
|
||||
expect(val).to.eq(undefined);
|
||||
expect(showInformationMessageSpy.getCalls().length).to.eq(5);
|
||||
done();
|
||||
})
|
||||
.catch((e) => fail(e));
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -567,6 +533,6 @@ describe("walkDirectory", () => {
|
||||
}
|
||||
|
||||
// Only real files should be returned.
|
||||
expect(files.sort()).to.deep.eq([file1, file2, file3, file4, file5, file6]);
|
||||
expect(files.sort()).toEqual([file1, file2, file3, file4, file5, file6]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { env } from "vscode";
|
||||
import { expect } from "chai";
|
||||
import { QueryHistoryConfig } from "../../config";
|
||||
import { HistoryItemLabelProvider } from "../../history-item-label-provider";
|
||||
import { createMockLocalQueryInfo } from "../factories/local-queries/local-query-history-item";
|
||||
@@ -30,15 +29,15 @@ describe("HistoryItemLabelProvider", () => {
|
||||
hasMetadata: true,
|
||||
});
|
||||
|
||||
expect(labelProvider.getLabel(fqi)).to.eq("user-specified-name");
|
||||
expect(labelProvider.getLabel(fqi)).toBe("user-specified-name");
|
||||
|
||||
fqi.userSpecifiedLabel = "%t %q %d %s %f %r %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
|
||||
);
|
||||
|
||||
fqi.userSpecifiedLabel = "%t %q %d %s %f %r %%::%t %q %d %s %f %r %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %::${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
|
||||
);
|
||||
});
|
||||
@@ -50,15 +49,15 @@ describe("HistoryItemLabelProvider", () => {
|
||||
hasMetadata: true,
|
||||
});
|
||||
|
||||
expect(labelProvider.getLabel(fqi)).to.eq("xxx query-name xxx");
|
||||
expect(labelProvider.getLabel(fqi)).toBe("xxx query-name xxx");
|
||||
|
||||
config.format = "%t %q %d %s %f %r %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
|
||||
);
|
||||
|
||||
config.format = "%t %q %d %s %f %r %%::%t %q %d %s %f %r %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %::${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
|
||||
);
|
||||
});
|
||||
@@ -72,18 +71,18 @@ describe("HistoryItemLabelProvider", () => {
|
||||
});
|
||||
|
||||
// fall back on user specified if one exists.
|
||||
expect(labelProvider.getShortLabel(fqi)).to.eq("user-specified-name");
|
||||
expect(labelProvider.getShortLabel(fqi)).toBe("user-specified-name");
|
||||
|
||||
// use query name if no user-specified label exists
|
||||
fqi.userSpecifiedLabel = undefined;
|
||||
expect(labelProvider.getShortLabel(fqi)).to.eq("query-name");
|
||||
expect(labelProvider.getShortLabel(fqi)).toBe("query-name");
|
||||
|
||||
// use file name if no user-specified label exists and the query is not yet completed (meaning it has no results)
|
||||
const fqi2 = createMockLocalQueryInfo({
|
||||
startTime: date,
|
||||
hasMetadata: true,
|
||||
});
|
||||
expect(labelProvider.getShortLabel(fqi2)).to.eq("query-file.ql");
|
||||
expect(labelProvider.getShortLabel(fqi2)).toBe("query-file.ql");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,15 +90,15 @@ describe("HistoryItemLabelProvider", () => {
|
||||
it("should interpolate query when user specified", () => {
|
||||
const fqi = createMockRemoteQueryHistoryItem({ userSpecifiedLabel });
|
||||
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(userSpecifiedLabel);
|
||||
expect(labelProvider.getLabel(fqi)).toBe(userSpecifiedLabel);
|
||||
|
||||
fqi.userSpecifiedLabel = "%t %q %d %s %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name (javascript) github/vscode-codeql-integration-tests in progress %`,
|
||||
);
|
||||
|
||||
fqi.userSpecifiedLabel = "%t %q %d %s %%::%t %q %d %s %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name (javascript) github/vscode-codeql-integration-tests in progress %::${dateStr} query-name (javascript) github/vscode-codeql-integration-tests in progress %`,
|
||||
);
|
||||
});
|
||||
@@ -111,17 +110,17 @@ describe("HistoryItemLabelProvider", () => {
|
||||
resultCount: 16,
|
||||
});
|
||||
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
"xxx query-name (javascript) xxx",
|
||||
);
|
||||
|
||||
config.format = "%t %q %d %s %f %r %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name (javascript) github/vscode-codeql-integration-tests completed query-file.ql (16 results) %`,
|
||||
);
|
||||
|
||||
config.format = "%t %q %d %s %f %r %%::%t %q %d %s %f %r %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name (javascript) github/vscode-codeql-integration-tests completed query-file.ql (16 results) %::${dateStr} query-name (javascript) github/vscode-codeql-integration-tests completed query-file.ql (16 results) %`,
|
||||
);
|
||||
});
|
||||
@@ -135,7 +134,7 @@ describe("HistoryItemLabelProvider", () => {
|
||||
});
|
||||
|
||||
config.format = "%t %q %d %s %f %r %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name (javascript) 2 repositories completed query-file.ql (16 results) %`,
|
||||
);
|
||||
});
|
||||
@@ -148,12 +147,12 @@ describe("HistoryItemLabelProvider", () => {
|
||||
});
|
||||
|
||||
// fall back on user specified if one exists.
|
||||
expect(labelProvider.getShortLabel(fqi)).to.eq("user-specified-name");
|
||||
expect(labelProvider.getShortLabel(fqi)).toBe("user-specified-name");
|
||||
|
||||
// use query name if no user-specified label exists
|
||||
const fqi2 = createMockRemoteQueryHistoryItem({});
|
||||
|
||||
expect(labelProvider.getShortLabel(fqi2)).to.eq("query-name");
|
||||
expect(labelProvider.getShortLabel(fqi2)).toBe("query-name");
|
||||
});
|
||||
|
||||
describe("when results are present", () => {
|
||||
@@ -165,7 +164,7 @@ describe("HistoryItemLabelProvider", () => {
|
||||
repositoryCount: 2,
|
||||
});
|
||||
config.format = "%t %q %d %s %f %r %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name (javascript) 2 repositories completed query-file.ql (16 results) %`,
|
||||
);
|
||||
});
|
||||
@@ -180,7 +179,7 @@ describe("HistoryItemLabelProvider", () => {
|
||||
repositoryCount: 2,
|
||||
});
|
||||
config.format = "%t %q %d %s %f %r %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name (javascript) 2 repositories completed query-file.ql %`,
|
||||
);
|
||||
});
|
||||
@@ -195,7 +194,7 @@ describe("HistoryItemLabelProvider", () => {
|
||||
repositoryCount: 2,
|
||||
});
|
||||
config.format = "%t %q %d %s %f %r %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name (javascript) 2 repositories completed query-file.ql %`,
|
||||
);
|
||||
});
|
||||
@@ -210,7 +209,7 @@ describe("HistoryItemLabelProvider", () => {
|
||||
repositoryCount: 2,
|
||||
});
|
||||
config.format = " %t %q %d %s %f %r %%";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
` ${dateStr} query-name (javascript) 2 repositories completed query-file.ql %`,
|
||||
);
|
||||
});
|
||||
@@ -225,7 +224,7 @@ describe("HistoryItemLabelProvider", () => {
|
||||
repositoryCount: 2,
|
||||
});
|
||||
config.format = "%t %q %d %s %f %r %% ";
|
||||
expect(labelProvider.getLabel(fqi)).to.eq(
|
||||
expect(labelProvider.getLabel(fqi)).toBe(
|
||||
`${dateStr} query-name (javascript) 2 repositories completed query-file.ql % `,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
import "source-map-support/register";
|
||||
import * as sinonChai from "sinon-chai";
|
||||
import * as chai from "chai";
|
||||
import * as chaiAsPromised from "chai-as-promised";
|
||||
import "chai/register-should";
|
||||
import { ExtensionContext } from "vscode";
|
||||
|
||||
import { runTestsInDirectory } from "../index-template";
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
chai.use(sinonChai);
|
||||
|
||||
export function run(): Promise<void> {
|
||||
return runTestsInDirectory(__dirname);
|
||||
}
|
||||
|
||||
export function createMockExtensionContext(): ExtensionContext {
|
||||
return {
|
||||
globalState: {
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { expect } from "chai";
|
||||
import * as vscode from "vscode";
|
||||
import * as path from "path";
|
||||
import * as sinon from "sinon";
|
||||
import * as tmp from "tmp";
|
||||
import { window, ViewColumn, Uri } from "vscode";
|
||||
import { window, ViewColumn, Uri, WebviewPanel } from "vscode";
|
||||
import { fileUriToWebviewUri, tryResolveLocation } from "../../interface-utils";
|
||||
import { getDefaultResultSetName } from "../../pure/interface-types";
|
||||
import { DatabaseItem } from "../../databases";
|
||||
import { FileResult } from "tmp";
|
||||
|
||||
describe("interface-utils", () => {
|
||||
describe("webview uri conversion", function () {
|
||||
describe("webview uri conversion", () => {
|
||||
const fileSuffix = ".bqrs";
|
||||
|
||||
function setupWebview(filePrefix: string) {
|
||||
@@ -29,11 +28,6 @@ describe("interface-utils", () => {
|
||||
},
|
||||
);
|
||||
|
||||
after(function () {
|
||||
panel.dispose();
|
||||
tmpFile.removeCallback();
|
||||
});
|
||||
|
||||
// CSP allowing nothing, to prevent warnings.
|
||||
const html =
|
||||
'<html><head><meta http-equiv="Content-Security-Policy" content="default-src \'none\';"></head></html>';
|
||||
@@ -41,14 +35,28 @@ describe("interface-utils", () => {
|
||||
return {
|
||||
fileUriOnDisk,
|
||||
panel,
|
||||
tmpFile,
|
||||
};
|
||||
}
|
||||
|
||||
it("does not double-encode # in URIs", function () {
|
||||
const { fileUriOnDisk, panel } = setupWebview("#");
|
||||
let webview: {
|
||||
fileUriOnDisk: Uri;
|
||||
panel: WebviewPanel;
|
||||
tmpFile: FileResult;
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
webview?.panel.dispose();
|
||||
webview?.tmpFile?.removeCallback();
|
||||
});
|
||||
|
||||
it("does not double-encode # in URIs", () => {
|
||||
webview = setupWebview("#");
|
||||
|
||||
const { fileUriOnDisk, panel } = webview;
|
||||
const webviewUri = fileUriToWebviewUri(panel, fileUriOnDisk);
|
||||
const parsedUri = Uri.parse(webviewUri);
|
||||
expect(path.basename(parsedUri.path, fileSuffix)).to.equal(
|
||||
expect(path.basename(parsedUri.path, fileSuffix)).toBe(
|
||||
path.basename(fileUriOnDisk.path, fileSuffix),
|
||||
);
|
||||
});
|
||||
@@ -56,25 +64,23 @@ describe("interface-utils", () => {
|
||||
|
||||
describe("getDefaultResultSetName", () => {
|
||||
it("should get the default name", () => {
|
||||
expect(getDefaultResultSetName(["a", "b", "#select", "alerts"])).to.equal(
|
||||
expect(getDefaultResultSetName(["a", "b", "#select", "alerts"])).toBe(
|
||||
"alerts",
|
||||
);
|
||||
expect(getDefaultResultSetName(["a", "b", "#select"])).to.equal(
|
||||
"#select",
|
||||
);
|
||||
expect(getDefaultResultSetName(["a", "b"])).to.equal("a");
|
||||
expect(getDefaultResultSetName([])).to.be.undefined;
|
||||
expect(getDefaultResultSetName(["a", "b", "#select"])).toBe("#select");
|
||||
expect(getDefaultResultSetName(["a", "b"])).toBe("a");
|
||||
expect(getDefaultResultSetName([])).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveWholeFileLocation", () => {
|
||||
it("should resolve a whole file location", () => {
|
||||
const mockDatabaseItem: DatabaseItem = {
|
||||
resolveSourceFile: sinon.stub().returns(vscode.Uri.file("abc")),
|
||||
resolveSourceFile: jest.fn().mockReturnValue(vscode.Uri.file("abc")),
|
||||
} as unknown as DatabaseItem;
|
||||
expect(
|
||||
tryResolveLocation("file://hucairz:0:0:0:0", mockDatabaseItem),
|
||||
).to.deep.equal(
|
||||
).toEqual(
|
||||
new vscode.Location(
|
||||
vscode.Uri.file("abc"),
|
||||
new vscode.Range(0, 0, 0, 0),
|
||||
@@ -84,11 +90,11 @@ describe("interface-utils", () => {
|
||||
|
||||
it("should resolve a five-part location edge case", () => {
|
||||
const mockDatabaseItem: DatabaseItem = {
|
||||
resolveSourceFile: sinon.stub().returns(vscode.Uri.file("abc")),
|
||||
resolveSourceFile: jest.fn().mockReturnValue(vscode.Uri.file("abc")),
|
||||
} as unknown as DatabaseItem;
|
||||
expect(
|
||||
tryResolveLocation("file://hucairz:1:1:1:1", mockDatabaseItem),
|
||||
).to.deep.equal(
|
||||
).toEqual(
|
||||
new vscode.Location(
|
||||
vscode.Uri.file("abc"),
|
||||
new vscode.Range(0, 0, 0, 1),
|
||||
@@ -98,7 +104,7 @@ describe("interface-utils", () => {
|
||||
|
||||
it("should resolve a five-part location", () => {
|
||||
const mockDatabaseItem: DatabaseItem = {
|
||||
resolveSourceFile: sinon.stub().returns(vscode.Uri.parse("abc")),
|
||||
resolveSourceFile: jest.fn().mockReturnValue(vscode.Uri.parse("abc")),
|
||||
} as unknown as DatabaseItem;
|
||||
|
||||
expect(
|
||||
@@ -112,7 +118,7 @@ describe("interface-utils", () => {
|
||||
},
|
||||
mockDatabaseItem,
|
||||
),
|
||||
).to.deep.equal(
|
||||
).toEqual(
|
||||
new vscode.Location(
|
||||
vscode.Uri.parse("abc"),
|
||||
new vscode.Range(
|
||||
@@ -121,14 +127,15 @@ describe("interface-utils", () => {
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(mockDatabaseItem.resolveSourceFile).to.have.been.calledOnceWith(
|
||||
expect(mockDatabaseItem.resolveSourceFile).toHaveBeenCalledTimes(1);
|
||||
expect(mockDatabaseItem.resolveSourceFile).toHaveBeenCalledWith(
|
||||
"hucairz",
|
||||
);
|
||||
});
|
||||
|
||||
it("should resolve a five-part location with an empty path", () => {
|
||||
const mockDatabaseItem: DatabaseItem = {
|
||||
resolveSourceFile: sinon.stub().returns(vscode.Uri.parse("abc")),
|
||||
resolveSourceFile: jest.fn().mockReturnValue(vscode.Uri.parse("abc")),
|
||||
} as unknown as DatabaseItem;
|
||||
|
||||
expect(
|
||||
@@ -142,35 +149,36 @@ describe("interface-utils", () => {
|
||||
},
|
||||
mockDatabaseItem,
|
||||
),
|
||||
).to.be.undefined;
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should resolve a string location for whole file", () => {
|
||||
const mockDatabaseItem: DatabaseItem = {
|
||||
resolveSourceFile: sinon.stub().returns(vscode.Uri.parse("abc")),
|
||||
resolveSourceFile: jest.fn().mockReturnValue(vscode.Uri.parse("abc")),
|
||||
} as unknown as DatabaseItem;
|
||||
|
||||
expect(
|
||||
tryResolveLocation("file://hucairz:0:0:0:0", mockDatabaseItem),
|
||||
).to.deep.equal(
|
||||
).toEqual(
|
||||
new vscode.Location(
|
||||
vscode.Uri.parse("abc"),
|
||||
new vscode.Range(0, 0, 0, 0),
|
||||
),
|
||||
);
|
||||
expect(mockDatabaseItem.resolveSourceFile).to.have.been.calledOnceWith(
|
||||
expect(mockDatabaseItem.resolveSourceFile).toHaveBeenCalledTimes(1);
|
||||
expect(mockDatabaseItem.resolveSourceFile).toHaveBeenCalledWith(
|
||||
"hucairz",
|
||||
);
|
||||
});
|
||||
|
||||
it("should resolve a string location for five-part location", () => {
|
||||
const mockDatabaseItem: DatabaseItem = {
|
||||
resolveSourceFile: sinon.stub().returns(vscode.Uri.parse("abc")),
|
||||
resolveSourceFile: jest.fn().mockReturnValue(vscode.Uri.parse("abc")),
|
||||
} as unknown as DatabaseItem;
|
||||
|
||||
expect(
|
||||
tryResolveLocation("file://hucairz:5:4:3:2", mockDatabaseItem),
|
||||
).to.deep.equal(
|
||||
).toEqual(
|
||||
new vscode.Location(
|
||||
vscode.Uri.parse("abc"),
|
||||
new vscode.Range(
|
||||
@@ -179,18 +187,20 @@ describe("interface-utils", () => {
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(mockDatabaseItem.resolveSourceFile).to.have.been.calledOnceWith(
|
||||
expect(mockDatabaseItem.resolveSourceFile).toHaveBeenCalledTimes(1);
|
||||
expect(mockDatabaseItem.resolveSourceFile).toHaveBeenCalledWith(
|
||||
"hucairz",
|
||||
);
|
||||
});
|
||||
|
||||
it("should resolve a string location for invalid string", () => {
|
||||
const mockDatabaseItem: DatabaseItem = {
|
||||
resolveSourceFile: sinon.stub().returns(vscode.Uri.parse("abc")),
|
||||
resolveSourceFile: jest.fn().mockReturnValue(vscode.Uri.parse("abc")),
|
||||
} as unknown as DatabaseItem;
|
||||
|
||||
expect(tryResolveLocation("file://hucairz:x:y:z:a", mockDatabaseItem)).to
|
||||
.be.undefined;
|
||||
expect(
|
||||
tryResolveLocation("file://hucairz:x:y:z:a", mockDatabaseItem),
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Config } from "jest";
|
||||
|
||||
import baseConfig from "../jest.config.base";
|
||||
|
||||
const config: Config = {
|
||||
...baseConfig,
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,4 +1,3 @@
|
||||
import { expect } from "chai";
|
||||
import { QueryStatus } from "../../query-status";
|
||||
import {
|
||||
buildRepoLabel,
|
||||
@@ -40,19 +39,19 @@ describe("Query history info", () => {
|
||||
it("should get the name for local history items", () => {
|
||||
const queryName = getRawQueryName(localQueryHistoryItem);
|
||||
|
||||
expect(queryName).to.equal(localQueryHistoryItem.getQueryName());
|
||||
expect(queryName).toBe(localQueryHistoryItem.getQueryName());
|
||||
});
|
||||
|
||||
it("should get the name for remote query history items", () => {
|
||||
const queryName = getRawQueryName(remoteQueryHistoryItem);
|
||||
|
||||
expect(queryName).to.equal(remoteQueryHistoryItem.remoteQuery.queryName);
|
||||
expect(queryName).toBe(remoteQueryHistoryItem.remoteQuery.queryName);
|
||||
});
|
||||
|
||||
it("should get the name for variant analysis history items", () => {
|
||||
const queryName = getRawQueryName(variantAnalysisHistoryItem);
|
||||
|
||||
expect(queryName).to.equal(
|
||||
expect(queryName).toBe(
|
||||
variantAnalysisHistoryItem.variantAnalysis.query.name,
|
||||
);
|
||||
});
|
||||
@@ -62,19 +61,19 @@ describe("Query history info", () => {
|
||||
it("should get the ID for local history items", () => {
|
||||
const historyItemId = getQueryId(localQueryHistoryItem);
|
||||
|
||||
expect(historyItemId).to.equal(localQueryHistoryItem.initialInfo.id);
|
||||
expect(historyItemId).toBe(localQueryHistoryItem.initialInfo.id);
|
||||
});
|
||||
|
||||
it("should get the ID for remote query history items", () => {
|
||||
const historyItemId = getQueryId(remoteQueryHistoryItem);
|
||||
|
||||
expect(historyItemId).to.equal(remoteQueryHistoryItem.queryId);
|
||||
expect(historyItemId).toBe(remoteQueryHistoryItem.queryId);
|
||||
});
|
||||
|
||||
it("should get the ID for variant analysis history items", () => {
|
||||
const historyItemId = getQueryId(variantAnalysisHistoryItem);
|
||||
|
||||
expect(historyItemId).to.equal(
|
||||
expect(historyItemId).toBe(
|
||||
variantAnalysisHistoryItem.variantAnalysis.id.toString(),
|
||||
);
|
||||
});
|
||||
@@ -84,19 +83,19 @@ describe("Query history info", () => {
|
||||
it("should get the query text for local history items", () => {
|
||||
const queryText = getQueryText(localQueryHistoryItem);
|
||||
|
||||
expect(queryText).to.equal(localQueryHistoryItem.initialInfo.queryText);
|
||||
expect(queryText).toBe(localQueryHistoryItem.initialInfo.queryText);
|
||||
});
|
||||
|
||||
it("should get the query text for remote query history items", () => {
|
||||
const queryText = getQueryText(remoteQueryHistoryItem);
|
||||
|
||||
expect(queryText).to.equal(remoteQueryHistoryItem.remoteQuery.queryText);
|
||||
expect(queryText).toBe(remoteQueryHistoryItem.remoteQuery.queryText);
|
||||
});
|
||||
|
||||
it("should get the query text for variant analysis history items", () => {
|
||||
const queryText = getQueryText(variantAnalysisHistoryItem);
|
||||
|
||||
expect(queryText).to.equal(
|
||||
expect(queryText).toBe(
|
||||
variantAnalysisHistoryItem.variantAnalysis.query.text,
|
||||
);
|
||||
});
|
||||
@@ -108,7 +107,7 @@ describe("Query history info", () => {
|
||||
const repoLabel = buildRepoLabel(remoteQueryHistoryItem);
|
||||
const expectedRepoLabel = `${remoteQueryHistoryItem.remoteQuery.controllerRepository.owner}/${remoteQueryHistoryItem.remoteQuery.controllerRepository.name}`;
|
||||
|
||||
expect(repoLabel).to.equal(expectedRepoLabel);
|
||||
expect(repoLabel).toBe(expectedRepoLabel);
|
||||
});
|
||||
it("should return number of repositories when `repositoryCount` is non-zero", () => {
|
||||
const remoteQueryHistoryItem2 = createMockRemoteQueryHistoryItem({
|
||||
@@ -117,7 +116,7 @@ describe("Query history info", () => {
|
||||
const repoLabel2 = buildRepoLabel(remoteQueryHistoryItem2);
|
||||
const expectedRepoLabel2 = "3 repositories";
|
||||
|
||||
expect(repoLabel2).to.equal(expectedRepoLabel2);
|
||||
expect(repoLabel2).toBe(expectedRepoLabel2);
|
||||
});
|
||||
});
|
||||
describe("repo label for variant analysis history items", () => {
|
||||
@@ -133,7 +132,7 @@ describe("Query history info", () => {
|
||||
};
|
||||
const repoLabel0 = buildRepoLabel(variantAnalysisHistoryItem0);
|
||||
|
||||
expect(repoLabel0).to.equal("0/0 repositories");
|
||||
expect(repoLabel0).toBe("0/0 repositories");
|
||||
});
|
||||
it("should return label when `totalScannedRepositoryCount` is 1", () => {
|
||||
const variantAnalysisHistoryItem1: VariantAnalysisHistoryItem = {
|
||||
@@ -149,12 +148,12 @@ describe("Query history info", () => {
|
||||
};
|
||||
|
||||
const repoLabel1 = buildRepoLabel(variantAnalysisHistoryItem1);
|
||||
expect(repoLabel1).to.equal("0/1 repository");
|
||||
expect(repoLabel1).toBe("0/1 repository");
|
||||
});
|
||||
it("should return label when `totalScannedRepositoryCount` is greater than 1", () => {
|
||||
const repoLabel = buildRepoLabel(variantAnalysisHistoryItem);
|
||||
|
||||
expect(repoLabel).to.equal("2/4 repositories");
|
||||
expect(repoLabel).toBe("2/4 repositories");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -167,7 +166,7 @@ describe("Query history info", () => {
|
||||
|
||||
const remoteQuery = remoteQueryHistoryItem.remoteQuery;
|
||||
const fullName = `${remoteQuery.controllerRepository.owner}/${remoteQuery.controllerRepository.name}`;
|
||||
expect(actionsWorkflowRunUrl).to.equal(
|
||||
expect(actionsWorkflowRunUrl).toBe(
|
||||
`https://github.com/${fullName}/actions/runs/${remoteQuery.actionsWorkflowRunId}`,
|
||||
);
|
||||
});
|
||||
@@ -179,7 +178,7 @@ describe("Query history info", () => {
|
||||
|
||||
const variantAnalysis = variantAnalysisHistoryItem.variantAnalysis;
|
||||
const fullName = variantAnalysis.controllerRepo.fullName;
|
||||
expect(actionsWorkflowRunUrl).to.equal(
|
||||
expect(actionsWorkflowRunUrl).toBe(
|
||||
`https://github.com/${fullName}/actions/runs/${variantAnalysis.actionsWorkflowRunId}`,
|
||||
);
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,6 @@
|
||||
import { expect } from "chai";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import * as os from "os";
|
||||
import * as sinon from "sinon";
|
||||
import {
|
||||
LocalQueryInfo,
|
||||
InitialQueryInfo,
|
||||
@@ -26,36 +24,27 @@ import {
|
||||
QueryInProgress,
|
||||
} from "../../legacy-query-server/run-queries";
|
||||
import { EvaluationResult, QueryResultType } from "../../pure/legacy-messages";
|
||||
import Sinon = require("sinon");
|
||||
import { sleep } from "../../pure/time";
|
||||
|
||||
describe("query-results", () => {
|
||||
let disposeSpy: sinon.SinonSpy;
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let queryPath: string;
|
||||
let cnt = 0;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
disposeSpy = sandbox.spy();
|
||||
queryPath = path.join(Uri.file(tmpDir.name).fsPath, `query-${cnt++}`);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("FullQueryInfo", () => {
|
||||
it("should get the query name", () => {
|
||||
const fqi = createMockFullQueryInfo();
|
||||
|
||||
// from the query path
|
||||
expect(fqi.getQueryName()).to.eq("hucairz");
|
||||
expect(fqi.getQueryName()).toBe("hucairz");
|
||||
|
||||
fqi.completeThisQuery(createMockQueryWithResults(queryPath));
|
||||
|
||||
// from the metadata
|
||||
expect(fqi.getQueryName()).to.eq("vwx");
|
||||
expect(fqi.getQueryName()).toBe("vwx");
|
||||
|
||||
// from quick eval position
|
||||
(fqi.initialInfo as any).quickEvalPosition = {
|
||||
@@ -63,16 +52,16 @@ describe("query-results", () => {
|
||||
endLine: 2,
|
||||
fileName: "/home/users/yz",
|
||||
};
|
||||
expect(fqi.getQueryName()).to.eq("Quick evaluation of yz:1-2");
|
||||
expect(fqi.getQueryName()).toBe("Quick evaluation of yz:1-2");
|
||||
(fqi.initialInfo as any).quickEvalPosition.endLine = 1;
|
||||
expect(fqi.getQueryName()).to.eq("Quick evaluation of yz:1");
|
||||
expect(fqi.getQueryName()).toBe("Quick evaluation of yz:1");
|
||||
});
|
||||
|
||||
it("should get the query file name", () => {
|
||||
const fqi = createMockFullQueryInfo();
|
||||
|
||||
// from the query path
|
||||
expect(fqi.getQueryFileName()).to.eq("hucairz");
|
||||
expect(fqi.getQueryFileName()).toBe("hucairz");
|
||||
|
||||
// from quick eval position
|
||||
(fqi.initialInfo as any).quickEvalPosition = {
|
||||
@@ -80,9 +69,9 @@ describe("query-results", () => {
|
||||
endLine: 2,
|
||||
fileName: "/home/users/yz",
|
||||
};
|
||||
expect(fqi.getQueryFileName()).to.eq("yz:1-2");
|
||||
expect(fqi.getQueryFileName()).toBe("yz:1-2");
|
||||
(fqi.initialInfo as any).quickEvalPosition.endLine = 1;
|
||||
expect(fqi.getQueryFileName()).to.eq("yz:1");
|
||||
expect(fqi.getQueryFileName()).toBe("yz:1");
|
||||
});
|
||||
|
||||
it("should get the getResultsPath", () => {
|
||||
@@ -92,7 +81,7 @@ describe("query-results", () => {
|
||||
const expectedResultsPath = path.join(queryPath, "results.bqrs");
|
||||
|
||||
// from results path
|
||||
expect(completedQuery.getResultsPath("zxa", false)).to.eq(
|
||||
expect(completedQuery.getResultsPath("zxa", false)).toBe(
|
||||
expectedResultsPath,
|
||||
);
|
||||
|
||||
@@ -101,12 +90,12 @@ describe("query-results", () => {
|
||||
} as SortedResultSetInfo;
|
||||
|
||||
// still from results path
|
||||
expect(completedQuery.getResultsPath("zxa", false)).to.eq(
|
||||
expect(completedQuery.getResultsPath("zxa", false)).toBe(
|
||||
expectedResultsPath,
|
||||
);
|
||||
|
||||
// from sortedResultsInfo
|
||||
expect(completedQuery.getResultsPath("zxa")).to.eq("bxa");
|
||||
expect(completedQuery.getResultsPath("zxa")).toBe("bxa");
|
||||
});
|
||||
|
||||
it("should format the statusString", () => {
|
||||
@@ -118,27 +107,23 @@ describe("query-results", () => {
|
||||
};
|
||||
|
||||
evalResult.message = "Tremendously";
|
||||
expect(formatLegacyMessage(evalResult)).to.eq("failed: Tremendously");
|
||||
expect(formatLegacyMessage(evalResult)).toBe("failed: Tremendously");
|
||||
|
||||
evalResult.resultType = QueryResultType.OTHER_ERROR;
|
||||
expect(formatLegacyMessage(evalResult)).to.eq("failed: Tremendously");
|
||||
expect(formatLegacyMessage(evalResult)).toBe("failed: Tremendously");
|
||||
|
||||
evalResult.resultType = QueryResultType.CANCELLATION;
|
||||
evalResult.evaluationTime = 2345;
|
||||
expect(formatLegacyMessage(evalResult)).to.eq(
|
||||
"cancelled after 2 seconds",
|
||||
);
|
||||
expect(formatLegacyMessage(evalResult)).toBe("cancelled after 2 seconds");
|
||||
|
||||
evalResult.resultType = QueryResultType.OOM;
|
||||
expect(formatLegacyMessage(evalResult)).to.eq("out of memory");
|
||||
expect(formatLegacyMessage(evalResult)).toBe("out of memory");
|
||||
|
||||
evalResult.resultType = QueryResultType.SUCCESS;
|
||||
expect(formatLegacyMessage(evalResult)).to.eq("finished in 2 seconds");
|
||||
expect(formatLegacyMessage(evalResult)).toBe("finished in 2 seconds");
|
||||
|
||||
evalResult.resultType = QueryResultType.TIMEOUT;
|
||||
expect(formatLegacyMessage(evalResult)).to.eq(
|
||||
"timed out after 2 seconds",
|
||||
);
|
||||
expect(formatLegacyMessage(evalResult)).toBe("timed out after 2 seconds");
|
||||
});
|
||||
it("should updateSortState", async () => {
|
||||
// setup
|
||||
@@ -148,7 +133,7 @@ describe("query-results", () => {
|
||||
);
|
||||
const completedQuery = fqi.completedQuery!;
|
||||
|
||||
const spy = sandbox.spy();
|
||||
const spy = jest.fn();
|
||||
const mockServer = {
|
||||
sortBqrs: spy,
|
||||
} as unknown as CodeQLCliServer;
|
||||
@@ -170,7 +155,7 @@ describe("query-results", () => {
|
||||
queryPath,
|
||||
"sortedResults-a-result-set-name.bqrs",
|
||||
);
|
||||
expect(spy).to.have.been.calledWith(
|
||||
expect(spy).toBeCalledWith(
|
||||
expectedResultsPath,
|
||||
expectedSortedResultsPath,
|
||||
"a-result-set-name",
|
||||
@@ -178,22 +163,20 @@ describe("query-results", () => {
|
||||
[sortState.sortDirection],
|
||||
);
|
||||
|
||||
expect(
|
||||
completedQuery.sortedResultsInfo["a-result-set-name"],
|
||||
).to.deep.equal({
|
||||
expect(completedQuery.sortedResultsInfo["a-result-set-name"]).toEqual({
|
||||
resultsPath: expectedSortedResultsPath,
|
||||
sortState,
|
||||
});
|
||||
|
||||
// delete the sort state
|
||||
await completedQuery.updateSortState(mockServer, "a-result-set-name");
|
||||
expect(Object.values(completedQuery.sortedResultsInfo).length).to.eq(0);
|
||||
expect(Object.values(completedQuery.sortedResultsInfo).length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("interpretResultsSarif", () => {
|
||||
let mockServer: CodeQLCliServer;
|
||||
let spy: Sinon.SinonExpectation;
|
||||
const spy = jest.fn();
|
||||
const metadata = {
|
||||
kind: "my-kind",
|
||||
id: "my-id" as string | undefined,
|
||||
@@ -204,8 +187,7 @@ describe("query-results", () => {
|
||||
const sourceInfo = {};
|
||||
|
||||
beforeEach(() => {
|
||||
spy = sandbox.mock();
|
||||
spy.returns({ a: "1234" });
|
||||
spy.mockReset().mockReturnValue({ a: "1234" });
|
||||
|
||||
mockServer = {
|
||||
interpretBqrsSarif: spy,
|
||||
@@ -213,13 +195,12 @@ describe("query-results", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
sandbox.restore();
|
||||
safeDel(interpretedResultsPath);
|
||||
});
|
||||
|
||||
it("should interpretResultsSarif", async function () {
|
||||
it("should interpretResultsSarif", async () => {
|
||||
// up to 2 minutes per test
|
||||
this.timeout(2 * 60 * 1000);
|
||||
jest.setTimeout(2 * 60 * 1000);
|
||||
|
||||
const results = await interpretResultsSarif(
|
||||
mockServer,
|
||||
@@ -231,8 +212,8 @@ describe("query-results", () => {
|
||||
sourceInfo as SourceInfo,
|
||||
);
|
||||
|
||||
expect(results).to.deep.eq({ a: "1234", t: "SarifInterpretationData" });
|
||||
expect(spy).to.have.been.calledWith(
|
||||
expect(results).toEqual({ a: "1234", t: "SarifInterpretationData" });
|
||||
expect(spy).toBeCalledWith(
|
||||
metadata,
|
||||
resultsPath,
|
||||
interpretedResultsPath,
|
||||
@@ -240,9 +221,9 @@ describe("query-results", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should interpretBqrsSarif without ID", async function () {
|
||||
it("should interpretBqrsSarif without ID", async () => {
|
||||
// up to 2 minutes per test
|
||||
this.timeout(2 * 60 * 1000);
|
||||
jest.setTimeout(2 * 60 * 1000);
|
||||
|
||||
delete metadata.id;
|
||||
const results = await interpretResultsSarif(
|
||||
@@ -254,8 +235,8 @@ describe("query-results", () => {
|
||||
},
|
||||
sourceInfo as SourceInfo,
|
||||
);
|
||||
expect(results).to.deep.eq({ a: "1234", t: "SarifInterpretationData" });
|
||||
expect(spy).to.have.been.calledWith(
|
||||
expect(results).toEqual({ a: "1234", t: "SarifInterpretationData" });
|
||||
expect(spy).toBeCalledWith(
|
||||
{ kind: "my-kind", id: "dummy-id", scored: undefined },
|
||||
resultsPath,
|
||||
interpretedResultsPath,
|
||||
@@ -263,9 +244,9 @@ describe("query-results", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should use sarifParser on a valid small SARIF file", async function () {
|
||||
it("should use sarifParser on a valid small SARIF file", async () => {
|
||||
// up to 2 minutes per test
|
||||
this.timeout(2 * 60 * 1000);
|
||||
jest.setTimeout(2 * 60 * 1000);
|
||||
|
||||
fs.writeFileSync(
|
||||
interpretedResultsPath,
|
||||
@@ -284,15 +265,15 @@ describe("query-results", () => {
|
||||
sourceInfo as SourceInfo,
|
||||
);
|
||||
// We do not re-interpret if we are reading from a SARIF file.
|
||||
expect(spy).to.not.have.been.called;
|
||||
expect(spy).not.toBeCalled();
|
||||
|
||||
expect(results).to.have.property("t", "SarifInterpretationData");
|
||||
expect(results).to.have.nested.property("runs[0].results");
|
||||
expect(results).toHaveProperty("t", "SarifInterpretationData");
|
||||
expect(results).toHaveProperty("runs[0].results");
|
||||
});
|
||||
|
||||
it("should throw an error on an invalid small SARIF file", async function () {
|
||||
it("should throw an error on an invalid small SARIF file", async () => {
|
||||
// up to 2 minutes per test
|
||||
this.timeout(2 * 60 * 1000);
|
||||
jest.setTimeout(2 * 60 * 1000);
|
||||
|
||||
fs.writeFileSync(
|
||||
interpretedResultsPath,
|
||||
@@ -312,17 +293,17 @@ describe("query-results", () => {
|
||||
},
|
||||
sourceInfo as SourceInfo,
|
||||
),
|
||||
).to.be.rejectedWith(
|
||||
).rejects.toThrow(
|
||||
"Parsing output of interpretation failed: Invalid SARIF file: expecting at least one run with result.",
|
||||
);
|
||||
|
||||
// We do not attempt to re-interpret if we are reading from a SARIF file.
|
||||
expect(spy).to.not.have.been.called;
|
||||
expect(spy).not.toBeCalled();
|
||||
});
|
||||
|
||||
it("should use sarifParser on a valid large SARIF file", async function () {
|
||||
it("should use sarifParser on a valid large SARIF file", async () => {
|
||||
// up to 2 minutes per test
|
||||
this.timeout(2 * 60 * 1000);
|
||||
jest.setTimeout(2 * 60 * 1000);
|
||||
|
||||
const validSarifStream = fs.createWriteStream(interpretedResultsPath, {
|
||||
flags: "w",
|
||||
@@ -373,15 +354,15 @@ describe("query-results", () => {
|
||||
sourceInfo as SourceInfo,
|
||||
);
|
||||
// We do not re-interpret if we are reading from a SARIF file.
|
||||
expect(spy).to.not.have.been.called;
|
||||
expect(spy).not.toBeCalled();
|
||||
|
||||
expect(results).to.have.property("t", "SarifInterpretationData");
|
||||
expect(results).to.have.nested.property("runs[0].results");
|
||||
expect(results).toHaveProperty("t", "SarifInterpretationData");
|
||||
expect(results).toHaveProperty("runs[0].results");
|
||||
});
|
||||
|
||||
it("should throw an error on an invalid large SARIF file", async function () {
|
||||
it("should throw an error on an invalid large SARIF file", async () => {
|
||||
// up to 2 minutes per test
|
||||
this.timeout(2 * 60 * 1000);
|
||||
jest.setTimeout(2 * 60 * 1000);
|
||||
|
||||
// There is a problem on Windows where the file at the prior path isn't able
|
||||
// to be deleted or written to, so we rename the path for this last test.
|
||||
@@ -431,12 +412,12 @@ describe("query-results", () => {
|
||||
},
|
||||
sourceInfo as SourceInfo,
|
||||
),
|
||||
).to.be.rejectedWith(
|
||||
).rejects.toThrow(
|
||||
"Parsing output of interpretation failed: Invalid SARIF file: expecting at least one run with result.",
|
||||
);
|
||||
|
||||
// We do not attempt to re-interpret if we are reading from a SARIF file.
|
||||
expect(spy).to.not.have.been.called;
|
||||
expect(spy).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -532,9 +513,9 @@ describe("query-results", () => {
|
||||
|
||||
// make the diffs somewhat sane by comparing each element directly
|
||||
for (let i = 0; i < allHistoryActual.length; i++) {
|
||||
expect(allHistoryActual[i]).to.deep.eq(expectedHistory[i]);
|
||||
expect(allHistoryActual[i]).toEqual(expectedHistory[i]);
|
||||
}
|
||||
expect(allHistoryActual.length).to.deep.eq(expectedHistory.length);
|
||||
expect(allHistoryActual.length).toEqual(expectedHistory.length);
|
||||
});
|
||||
|
||||
it("should handle an invalid query history version", async () => {
|
||||
@@ -550,7 +531,7 @@ describe("query-results", () => {
|
||||
|
||||
const allHistoryActual = await slurpQueryHistory(badPath);
|
||||
// version number is invalid. Should return an empty array.
|
||||
expect(allHistoryActual).to.deep.eq([]);
|
||||
expect(allHistoryActual).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -589,7 +570,7 @@ describe("query-results", () => {
|
||||
query: query.queryEvalInfo,
|
||||
successful: didRunSuccessfully,
|
||||
message: "foo",
|
||||
dispose: disposeSpy,
|
||||
dispose: jest.fn(),
|
||||
result: {
|
||||
evaluationTime: 1,
|
||||
queryId: 0,
|
||||
|
||||
@@ -1,50 +1,24 @@
|
||||
import { expect } from "chai";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import * as sinon from "sinon";
|
||||
import * as pq from "proxyquire";
|
||||
import { ExtensionContext } from "vscode";
|
||||
import { createMockExtensionContext } from "../index";
|
||||
import { Credentials } from "../../../authentication";
|
||||
import { MarkdownFile } from "../../../remote-queries/remote-queries-markdown-generation";
|
||||
import * as markdownGenerator from "../../../remote-queries/remote-queries-markdown-generation";
|
||||
import * as ghApiClient from "../../../remote-queries/gh-api/gh-api-client";
|
||||
import { exportRemoteQueryAnalysisResults } from "../../../remote-queries/export-results";
|
||||
|
||||
const proxyquire = pq.noPreserveCache();
|
||||
describe("export results", () => {
|
||||
describe("exportRemoteQueryAnalysisResults", () => {
|
||||
const mockCredentials = {} as unknown as Credentials;
|
||||
|
||||
describe("export results", async function () {
|
||||
describe("exportRemoteQueryAnalysisResults", async function () {
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let mockCredentials: Credentials;
|
||||
let mockResponse: sinon.SinonStub<any, Promise<{ status: number }>>;
|
||||
let mockCreateGist: sinon.SinonStub;
|
||||
let ctx: ExtensionContext;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
|
||||
mockCredentials = {
|
||||
getOctokit: () =>
|
||||
Promise.resolve({
|
||||
request: mockResponse,
|
||||
}),
|
||||
} as unknown as Credentials;
|
||||
sandbox.stub(Credentials, "initialize").resolves(mockCredentials);
|
||||
|
||||
const resultFiles = [] as MarkdownFile[];
|
||||
proxyquire("../../../remote-queries/remote-queries-markdown-generation", {
|
||||
generateMarkdown: sinon.stub().returns(resultFiles),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
jest.spyOn(markdownGenerator, "generateMarkdown").mockReturnValue([]);
|
||||
jest.spyOn(Credentials, "initialize").mockResolvedValue(mockCredentials);
|
||||
|
||||
it("should call the GitHub Actions API with the correct gist title", async function () {
|
||||
mockCreateGist = sinon.stub(ghApiClient, "createGist");
|
||||
const mockCreateGist = jest
|
||||
.spyOn(ghApiClient, "createGist")
|
||||
.mockResolvedValue(undefined);
|
||||
|
||||
ctx = createMockExtensionContext();
|
||||
const ctx = createMockExtensionContext();
|
||||
const query = JSON.parse(
|
||||
await fs.readFile(
|
||||
path.join(
|
||||
@@ -72,9 +46,11 @@ describe("export results", async function () {
|
||||
"gist",
|
||||
);
|
||||
|
||||
expect(mockCreateGist.calledOnce).to.be.true;
|
||||
expect(mockCreateGist.firstCall.args[1]).to.equal(
|
||||
expect(mockCreateGist).toHaveBeenCalledTimes(1);
|
||||
expect(mockCreateGist).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
"Shell command built from environment values (javascript) 3 results (10 repositories)",
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { fail } from "assert";
|
||||
import { expect } from "chai";
|
||||
import * as sinon from "sinon";
|
||||
import { Credentials } from "../../../../authentication";
|
||||
import {
|
||||
cancelRemoteQuery,
|
||||
@@ -11,46 +9,43 @@ import { RemoteQuery } from "../../../../remote-queries/remote-query";
|
||||
import { createMockVariantAnalysis } from "../../../factories/remote-queries/shared/variant-analysis";
|
||||
import { VariantAnalysis } from "../../../../remote-queries/shared/variant-analysis";
|
||||
|
||||
jest.setTimeout(10000);
|
||||
|
||||
describe("gh-actions-api-client mock responses", () => {
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let mockCredentials: Credentials;
|
||||
let mockResponse: sinon.SinonStub<any, Promise<{ status: number }>>;
|
||||
const mockRequest = jest.fn();
|
||||
const mockCredentials = {
|
||||
getOctokit: () =>
|
||||
Promise.resolve({
|
||||
request: mockRequest,
|
||||
}),
|
||||
} as unknown as Credentials;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
mockCredentials = {
|
||||
getOctokit: () =>
|
||||
Promise.resolve({
|
||||
request: mockResponse,
|
||||
}),
|
||||
} as unknown as Credentials;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
mockRequest.mockReset();
|
||||
});
|
||||
|
||||
describe("cancelRemoteQuery", () => {
|
||||
it("should cancel a remote query", async () => {
|
||||
mockResponse = sinon.stub().resolves({ status: 202 });
|
||||
mockRequest.mockReturnValue({ status: 202 });
|
||||
await cancelRemoteQuery(mockCredentials, createMockRemoteQuery());
|
||||
|
||||
expect(mockResponse.calledOnce).to.be.true;
|
||||
expect(mockResponse.firstCall.args[0]).to.equal(
|
||||
expect(mockRequest).toHaveBeenCalledTimes(1);
|
||||
expect(mockRequest).toHaveBeenCalledWith(
|
||||
"POST /repos/github/codeql/actions/runs/123/cancel",
|
||||
);
|
||||
});
|
||||
|
||||
it("should fail to cancel a remote query", async () => {
|
||||
mockResponse = sinon
|
||||
.stub()
|
||||
.resolves({ status: 409, data: { message: "Uh oh!" } });
|
||||
mockRequest.mockResolvedValue({
|
||||
status: 409,
|
||||
data: { message: "Uh oh!" },
|
||||
});
|
||||
|
||||
await expect(
|
||||
cancelRemoteQuery(mockCredentials, createMockRemoteQuery()),
|
||||
).to.be.rejectedWith(/Error cancelling variant analysis: 409 Uh oh!/);
|
||||
expect(mockResponse.calledOnce).to.be.true;
|
||||
expect(mockResponse.firstCall.args[0]).to.equal(
|
||||
).rejects.toThrow(/Error cancelling variant analysis: 409 Uh oh!/);
|
||||
expect(mockRequest).toHaveBeenCalledTimes(1);
|
||||
expect(mockRequest).toHaveBeenCalledWith(
|
||||
"POST /repos/github/codeql/actions/runs/123/cancel",
|
||||
);
|
||||
});
|
||||
@@ -68,39 +63,38 @@ describe("gh-actions-api-client mock responses", () => {
|
||||
|
||||
describe("cancelVariantAnalysis", () => {
|
||||
let variantAnalysis: VariantAnalysis;
|
||||
before(() => {
|
||||
beforeAll(() => {
|
||||
variantAnalysis = createMockVariantAnalysis({});
|
||||
});
|
||||
|
||||
it("should cancel a variant analysis", async () => {
|
||||
mockResponse = sinon.stub().resolves({ status: 202 });
|
||||
mockRequest.mockResolvedValue({ status: 202 });
|
||||
await cancelVariantAnalysis(mockCredentials, variantAnalysis);
|
||||
|
||||
expect(mockResponse.calledOnce).to.be.true;
|
||||
expect(mockResponse.firstCall.args[0]).to.equal(
|
||||
expect(mockRequest).toHaveBeenCalledTimes(1);
|
||||
expect(mockRequest).toHaveBeenCalledWith(
|
||||
`POST /repos/${variantAnalysis.controllerRepo.fullName}/actions/runs/${variantAnalysis.actionsWorkflowRunId}/cancel`,
|
||||
);
|
||||
});
|
||||
|
||||
it("should fail to cancel a variant analysis", async () => {
|
||||
mockResponse = sinon
|
||||
.stub()
|
||||
.resolves({ status: 409, data: { message: "Uh oh!" } });
|
||||
mockRequest.mockResolvedValue({
|
||||
status: 409,
|
||||
data: { message: "Uh oh!" },
|
||||
});
|
||||
|
||||
await expect(
|
||||
cancelVariantAnalysis(mockCredentials, variantAnalysis),
|
||||
).to.be.rejectedWith(/Error cancelling variant analysis: 409 Uh oh!/);
|
||||
expect(mockResponse.calledOnce).to.be.true;
|
||||
expect(mockResponse.firstCall.args[0]).to.equal(
|
||||
).rejects.toThrow(/Error cancelling variant analysis: 409 Uh oh!/);
|
||||
expect(mockRequest).toHaveBeenCalledTimes(1);
|
||||
expect(mockRequest).toHaveBeenCalledWith(
|
||||
`POST /repos/${variantAnalysis.controllerRepo.fullName}/actions/runs/${variantAnalysis.actionsWorkflowRunId}/cancel`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("gh-actions-api-client real responses", function () {
|
||||
this.timeout(10000);
|
||||
|
||||
describe("gh-actions-api-client real responses", () => {
|
||||
it("should get the stargazers for repos", async () => {
|
||||
if (skip()) {
|
||||
return;
|
||||
@@ -123,7 +117,7 @@ describe("gh-actions-api-client real responses", function () {
|
||||
);
|
||||
|
||||
const stargazersKeys = Object.keys(stargazers).sort();
|
||||
expect(stargazersKeys).to.deep.eq([
|
||||
expect(stargazersKeys).toEqual([
|
||||
"angular/angular",
|
||||
"github/codeql",
|
||||
"github/vscode-codeql",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { expect } from "chai";
|
||||
import * as os from "os";
|
||||
import { parseResponse } from "../../../remote-queries/remote-queries-api";
|
||||
import { Repository } from "../../../remote-queries/shared/repository";
|
||||
@@ -16,10 +15,10 @@ describe("parseResponse", () => {
|
||||
repositories_queried: ["a/b", "c/d"],
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
expect(result.popupMessage).toBe(
|
||||
"Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).",
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
expect(result.logMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.",
|
||||
"",
|
||||
@@ -38,14 +37,14 @@ describe("parseResponse", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
expect(result.popupMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).",
|
||||
"",
|
||||
"Some repositories could not be scheduled. See extension log for details.",
|
||||
].join(os.EOL),
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
expect(result.logMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.",
|
||||
"",
|
||||
@@ -69,14 +68,14 @@ describe("parseResponse", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
expect(result.popupMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).",
|
||||
"",
|
||||
"Some repositories could not be scheduled. See extension log for details.",
|
||||
].join(os.EOL),
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
expect(result.logMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.",
|
||||
"",
|
||||
@@ -101,14 +100,14 @@ describe("parseResponse", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
expect(result.popupMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).",
|
||||
"",
|
||||
"Some repositories could not be scheduled. See extension log for details.",
|
||||
].join(os.EOL),
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
expect(result.logMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.",
|
||||
"",
|
||||
@@ -134,14 +133,14 @@ describe("parseResponse", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
expect(result.popupMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).",
|
||||
"",
|
||||
"Some repositories could not be scheduled. See extension log for details.",
|
||||
].join(os.EOL),
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
expect(result.logMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.",
|
||||
"",
|
||||
@@ -166,14 +165,14 @@ describe("parseResponse", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
expect(result.popupMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).",
|
||||
"",
|
||||
"Some repositories could not be scheduled. See extension log for details.",
|
||||
].join(os.EOL),
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
expect(result.logMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.",
|
||||
"",
|
||||
@@ -198,14 +197,14 @@ describe("parseResponse", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
expect(result.popupMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. [Click here to see the progress](https://github.com/org/name/actions/runs/123).",
|
||||
"",
|
||||
"Some repositories could not be scheduled. See extension log for details.",
|
||||
].join(os.EOL),
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
expect(result.logMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 2 repositories. See https://github.com/org/name/actions/runs/123.",
|
||||
"",
|
||||
@@ -237,14 +236,14 @@ describe("parseResponse", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.popupMessage).to.equal(
|
||||
expect(result.popupMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 1 repository. [Click here to see the progress](https://github.com/org/name/actions/runs/123).",
|
||||
"",
|
||||
"Some repositories could not be scheduled. See extension log for details.",
|
||||
].join(os.EOL),
|
||||
);
|
||||
expect(result.logMessage).to.equal(
|
||||
expect(result.logMessage).toBe(
|
||||
[
|
||||
"Successfully scheduled runs on 1 repository. See https://github.com/org/name/actions/runs/123.",
|
||||
"",
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import * as sinon from "sinon";
|
||||
import { expect } from "chai";
|
||||
|
||||
import {
|
||||
CancellationToken,
|
||||
ExtensionContext,
|
||||
TextDocument,
|
||||
TextEditor,
|
||||
Uri,
|
||||
window,
|
||||
workspace,
|
||||
@@ -32,62 +32,57 @@ import { VariantAnalysisManager } from "../../../remote-queries/variant-analysis
|
||||
* Tests for remote queries and how they interact with the query history manager.
|
||||
*/
|
||||
|
||||
describe("Remote queries and query history manager", function () {
|
||||
describe("Remote queries and query history manager", () => {
|
||||
const EXTENSION_PATH = path.join(__dirname, "../../../../");
|
||||
const STORAGE_DIR = Uri.file(path.join(tmpDir.name, "remote-queries")).fsPath;
|
||||
const asyncNoop = async () => {
|
||||
/** noop */
|
||||
};
|
||||
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let qhm: QueryHistoryManager;
|
||||
let localQueriesResultsViewStub: ResultsView;
|
||||
let remoteQueriesManagerStub: RemoteQueriesManager;
|
||||
let variantAnalysisManagerStub: VariantAnalysisManager;
|
||||
const localQueriesResultsViewStub = {
|
||||
showResults: jest.fn(),
|
||||
} as any as ResultsView;
|
||||
let rawQueryHistory: any;
|
||||
let remoteQueryResult0: RemoteQueryResult;
|
||||
let remoteQueryResult1: RemoteQueryResult;
|
||||
let disposables: DisposableBucket;
|
||||
let showTextDocumentSpy: sinon.SinonSpy;
|
||||
let openTextDocumentSpy: sinon.SinonSpy;
|
||||
|
||||
let rehydrateRemoteQueryStub: sinon.SinonStub;
|
||||
let removeRemoteQueryStub: sinon.SinonStub;
|
||||
let openRemoteQueryResultsStub: sinon.SinonStub;
|
||||
const rehydrateRemoteQueryStub = jest.fn();
|
||||
const removeRemoteQueryStub = jest.fn();
|
||||
const openRemoteQueryResultsStub = jest.fn();
|
||||
|
||||
beforeEach(async function () {
|
||||
const remoteQueriesManagerStub = {
|
||||
onRemoteQueryAdded: jest.fn(),
|
||||
onRemoteQueryRemoved: jest.fn(),
|
||||
onRemoteQueryStatusUpdate: jest.fn(),
|
||||
rehydrateRemoteQuery: rehydrateRemoteQueryStub,
|
||||
removeRemoteQuery: removeRemoteQueryStub,
|
||||
openRemoteQueryResults: openRemoteQueryResultsStub,
|
||||
} as any as RemoteQueriesManager;
|
||||
|
||||
const variantAnalysisManagerStub = {
|
||||
onVariantAnalysisAdded: jest.fn(),
|
||||
onVariantAnalysisStatusUpdated: jest.fn(),
|
||||
onVariantAnalysisRemoved: jest.fn(),
|
||||
} as any as VariantAnalysisManager;
|
||||
|
||||
const showTextDocumentSpy = jest.spyOn(window, "showTextDocument");
|
||||
const openTextDocumentSpy = jest.spyOn(workspace, "openTextDocument");
|
||||
|
||||
beforeEach(async () => {
|
||||
// set a higher timeout since recursive delete below may take a while, expecially on Windows.
|
||||
this.timeout(120000);
|
||||
jest.setTimeout(120000);
|
||||
|
||||
// Since these tests change the state of the query history manager, we need to copy the original
|
||||
// to a temporary folder where we can manipulate it for tests
|
||||
await copyHistoryState();
|
||||
|
||||
sandbox = sinon.createSandbox();
|
||||
disposables = new DisposableBucket();
|
||||
|
||||
localQueriesResultsViewStub = {
|
||||
showResults: sandbox.stub(),
|
||||
} as any as ResultsView;
|
||||
|
||||
rehydrateRemoteQueryStub = sandbox.stub();
|
||||
removeRemoteQueryStub = sandbox.stub();
|
||||
openRemoteQueryResultsStub = sandbox.stub();
|
||||
|
||||
remoteQueriesManagerStub = {
|
||||
onRemoteQueryAdded: sandbox.stub(),
|
||||
onRemoteQueryRemoved: sandbox.stub(),
|
||||
onRemoteQueryStatusUpdate: sandbox.stub(),
|
||||
rehydrateRemoteQuery: rehydrateRemoteQueryStub,
|
||||
removeRemoteQuery: removeRemoteQueryStub,
|
||||
openRemoteQueryResults: openRemoteQueryResultsStub,
|
||||
} as any as RemoteQueriesManager;
|
||||
|
||||
variantAnalysisManagerStub = {
|
||||
onVariantAnalysisAdded: sandbox.stub(),
|
||||
onVariantAnalysisStatusUpdated: sandbox.stub(),
|
||||
onVariantAnalysisRemoved: sandbox.stub(),
|
||||
} as any as VariantAnalysisManager;
|
||||
rehydrateRemoteQueryStub.mockReset();
|
||||
removeRemoteQueryStub.mockReset();
|
||||
openRemoteQueryResultsStub.mockReset();
|
||||
|
||||
rawQueryHistory = fs.readJSONSync(
|
||||
path.join(STORAGE_DIR, "workspace-query-history.json"),
|
||||
@@ -129,33 +124,38 @@ describe("Remote queries and query history manager", function () {
|
||||
);
|
||||
disposables.push(qhm);
|
||||
|
||||
showTextDocumentSpy = sandbox.spy(window, "showTextDocument");
|
||||
openTextDocumentSpy = sandbox.spy(workspace, "openTextDocument");
|
||||
showTextDocumentSpy.mockResolvedValue(undefined as unknown as TextEditor);
|
||||
openTextDocumentSpy.mockResolvedValue(undefined as unknown as TextDocument);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
afterEach(() => {
|
||||
// set a higher timeout since recursive delete below may take a while, expecially on Windows.
|
||||
this.timeout(120000);
|
||||
jest.setTimeout(120000);
|
||||
deleteHistoryState();
|
||||
disposables.dispose(testDisposeHandler);
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it("should read query history", async () => {
|
||||
await qhm.readQueryHistory();
|
||||
|
||||
// Should have added the query history. Contents are directly from the file
|
||||
expect(rehydrateRemoteQueryStub).to.have.callCount(2);
|
||||
expect(rehydrateRemoteQueryStub.getCall(0).args[1]).to.deep.eq(
|
||||
expect(rehydrateRemoteQueryStub).toBeCalledTimes(2);
|
||||
expect(rehydrateRemoteQueryStub).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
rawQueryHistory[0].queryId,
|
||||
rawQueryHistory[0].remoteQuery,
|
||||
rawQueryHistory[0].status,
|
||||
);
|
||||
expect(rehydrateRemoteQueryStub.getCall(1).args[1]).to.deep.eq(
|
||||
expect(rehydrateRemoteQueryStub).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
rawQueryHistory[1].queryId,
|
||||
rawQueryHistory[1].remoteQuery,
|
||||
rawQueryHistory[1].status,
|
||||
);
|
||||
|
||||
expect(qhm.treeDataProvider.allHistory[0]).to.deep.eq(rawQueryHistory[0]);
|
||||
expect(qhm.treeDataProvider.allHistory[1]).to.deep.eq(rawQueryHistory[1]);
|
||||
expect(qhm.treeDataProvider.allHistory.length).to.eq(2);
|
||||
expect(qhm.treeDataProvider.allHistory[0]).toEqual(rawQueryHistory[0]);
|
||||
expect(qhm.treeDataProvider.allHistory[1]).toEqual(rawQueryHistory[1]);
|
||||
expect(qhm.treeDataProvider.allHistory.length).toBe(2);
|
||||
});
|
||||
|
||||
it("should remove and then add query from history", async () => {
|
||||
@@ -164,28 +164,32 @@ describe("Remote queries and query history manager", function () {
|
||||
// Remove the first query
|
||||
await qhm.handleRemoveHistoryItem(qhm.treeDataProvider.allHistory[0]);
|
||||
|
||||
expect(removeRemoteQueryStub).calledOnceWithExactly(
|
||||
expect(removeRemoteQueryStub).toHaveBeenCalledWith(
|
||||
rawQueryHistory[0].queryId,
|
||||
);
|
||||
expect(rehydrateRemoteQueryStub).to.have.callCount(2);
|
||||
expect(rehydrateRemoteQueryStub.getCall(0).args[1]).to.deep.eq(
|
||||
expect(rehydrateRemoteQueryStub).toBeCalledTimes(2);
|
||||
expect(rehydrateRemoteQueryStub).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
rawQueryHistory[0].queryId,
|
||||
rawQueryHistory[0].remoteQuery,
|
||||
rawQueryHistory[0].status,
|
||||
);
|
||||
expect(rehydrateRemoteQueryStub.getCall(1).args[1]).to.deep.eq(
|
||||
expect(rehydrateRemoteQueryStub).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
rawQueryHistory[1].queryId,
|
||||
rawQueryHistory[1].remoteQuery,
|
||||
rawQueryHistory[1].status,
|
||||
);
|
||||
expect(openRemoteQueryResultsStub).calledOnceWithExactly(
|
||||
expect(openRemoteQueryResultsStub).toHaveBeenCalledWith(
|
||||
rawQueryHistory[1].queryId,
|
||||
);
|
||||
expect(qhm.treeDataProvider.allHistory).to.deep.eq(
|
||||
rawQueryHistory.slice(1),
|
||||
);
|
||||
expect(qhm.treeDataProvider.allHistory).toEqual(rawQueryHistory.slice(1));
|
||||
|
||||
// Add it back
|
||||
qhm.addQuery(rawQueryHistory[0]);
|
||||
expect(removeRemoteQueryStub).to.have.callCount(1);
|
||||
expect(rehydrateRemoteQueryStub).to.have.callCount(2);
|
||||
expect(qhm.treeDataProvider.allHistory).to.deep.eq([
|
||||
expect(removeRemoteQueryStub).toBeCalledTimes(1);
|
||||
expect(rehydrateRemoteQueryStub).toBeCalledTimes(2);
|
||||
expect(qhm.treeDataProvider.allHistory).toEqual([
|
||||
rawQueryHistory[1],
|
||||
rawQueryHistory[0],
|
||||
]);
|
||||
@@ -201,19 +205,21 @@ describe("Remote queries and query history manager", function () {
|
||||
qhm.treeDataProvider.allHistory[0],
|
||||
]);
|
||||
|
||||
expect(removeRemoteQueryStub.callCount).to.eq(2);
|
||||
expect(removeRemoteQueryStub.getCall(0).args[0]).to.eq(
|
||||
expect(removeRemoteQueryStub).toHaveBeenCalledTimes(2);
|
||||
expect(removeRemoteQueryStub).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
rawQueryHistory[1].queryId,
|
||||
);
|
||||
expect(removeRemoteQueryStub.getCall(1).args[0]).to.eq(
|
||||
expect(removeRemoteQueryStub).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
rawQueryHistory[0].queryId,
|
||||
);
|
||||
expect(qhm.treeDataProvider.allHistory).to.deep.eq([]);
|
||||
expect(qhm.treeDataProvider.allHistory).toEqual([]);
|
||||
|
||||
// also, both queries should be removed from on disk storage
|
||||
expect(
|
||||
fs.readJSONSync(path.join(STORAGE_DIR, "workspace-query-history.json")),
|
||||
).to.deep.eq({
|
||||
).toEqual({
|
||||
version: 2,
|
||||
queries: [],
|
||||
});
|
||||
@@ -223,7 +229,7 @@ describe("Remote queries and query history manager", function () {
|
||||
await qhm.readQueryHistory();
|
||||
|
||||
await qhm.handleItemClicked(qhm.treeDataProvider.allHistory[0], []);
|
||||
expect(openRemoteQueryResultsStub).calledOnceWithExactly(
|
||||
expect(openRemoteQueryResultsStub).toHaveBeenCalledWith(
|
||||
rawQueryHistory[0].queryId,
|
||||
);
|
||||
});
|
||||
@@ -232,14 +238,14 @@ describe("Remote queries and query history manager", function () {
|
||||
await qhm.readQueryHistory();
|
||||
await qhm.handleShowQueryText(qhm.treeDataProvider.allHistory[0], []);
|
||||
|
||||
expect(showTextDocumentSpy).to.have.been.calledOnce;
|
||||
expect(openTextDocumentSpy).to.have.been.calledOnce;
|
||||
expect(showTextDocumentSpy).toBeCalledTimes(1);
|
||||
expect(openTextDocumentSpy).toBeCalledTimes(1);
|
||||
|
||||
const uri: Uri = openTextDocumentSpy.getCall(0).args[0];
|
||||
expect(uri.scheme).to.eq("codeql");
|
||||
const uri: Uri = openTextDocumentSpy.mock.calls[0][0] as Uri;
|
||||
expect(uri.scheme).toBe("codeql");
|
||||
const params = new URLSearchParams(uri.query);
|
||||
expect(params.get("isQuickEval")).to.eq("false");
|
||||
expect(params.get("queryText")).to.eq(
|
||||
expect(params.get("isQuickEval")).toBe("false");
|
||||
expect(params.get("queryText")).toBe(
|
||||
rawQueryHistory[0].remoteQuery.queryText,
|
||||
);
|
||||
});
|
||||
@@ -253,19 +259,19 @@ describe("Remote queries and query history manager", function () {
|
||||
|
||||
beforeEach(() => {
|
||||
mockOctokit = {
|
||||
request: sandbox.stub(),
|
||||
request: jest.fn(),
|
||||
};
|
||||
mockCredentials = {
|
||||
getOctokit: () => mockOctokit,
|
||||
};
|
||||
mockLogger = {
|
||||
log: sandbox.spy(),
|
||||
log: jest.fn(),
|
||||
};
|
||||
mockCliServer = {
|
||||
bqrsInfo: sandbox.spy(),
|
||||
bqrsDecode: sandbox.spy(),
|
||||
bqrsInfo: jest.fn(),
|
||||
bqrsDecode: jest.fn(),
|
||||
};
|
||||
sandbox.stub(Credentials, "initialize").resolves(mockCredentials);
|
||||
jest.spyOn(Credentials, "initialize").mockResolvedValue(mockCredentials);
|
||||
|
||||
arm = new AnalysesResultsManager(
|
||||
{} as ExtensionContext,
|
||||
@@ -277,54 +283,60 @@ describe("Remote queries and query history manager", function () {
|
||||
|
||||
it("should avoid re-downloading an analysis result", async () => {
|
||||
// because the analysis result is already in on disk, it should not be downloaded
|
||||
const publisher = sandbox.spy();
|
||||
const publisher = jest.fn();
|
||||
const analysisSummary = remoteQueryResult0.analysisSummaries[0];
|
||||
await arm.downloadAnalysisResults(analysisSummary, publisher);
|
||||
|
||||
// Should not have made the request since the analysis result is already on disk
|
||||
expect(mockOctokit.request).to.not.have.been.called;
|
||||
expect(mockOctokit.request).not.toBeCalled();
|
||||
|
||||
// result should have been published twice
|
||||
expect(publisher).toHaveBeenCalledTimes(2);
|
||||
|
||||
// first time, it is in progress
|
||||
expect(publisher.getCall(0).args[0][0]).to.include({
|
||||
nwo: "github/vscode-codeql",
|
||||
status: "InProgress",
|
||||
// interpretedResults: ... avoid checking the interpretedResults object since it is complex
|
||||
});
|
||||
expect(publisher).toHaveBeenNthCalledWith(1, [
|
||||
expect.objectContaining({
|
||||
nwo: "github/vscode-codeql",
|
||||
status: "InProgress",
|
||||
interpretedResults: expect.anything(), // avoid checking the interpretedResults object since it is complex
|
||||
}),
|
||||
]);
|
||||
|
||||
// second time, it has the path to the sarif file.
|
||||
expect(publisher.getCall(1).args[0][0]).to.include({
|
||||
nwo: "github/vscode-codeql",
|
||||
status: "Completed",
|
||||
// interpretedResults: ... avoid checking the interpretedResults object since it is complex
|
||||
});
|
||||
expect(publisher).to.have.been.calledTwice;
|
||||
expect(publisher).toHaveBeenNthCalledWith(2, [
|
||||
expect.objectContaining({
|
||||
nwo: "github/vscode-codeql",
|
||||
status: "Completed",
|
||||
interpretedResults: expect.anything(), // avoid checking the interpretedResults object since it is complex
|
||||
}),
|
||||
]);
|
||||
|
||||
// result should be stored in the manager
|
||||
expect(arm.getAnalysesResults(rawQueryHistory[0].queryId)[0]).to.include({
|
||||
expect(
|
||||
arm.getAnalysesResults(rawQueryHistory[0].queryId)[0],
|
||||
).toMatchObject({
|
||||
nwo: "github/vscode-codeql",
|
||||
status: "Completed",
|
||||
// interpretedResults: ... avoid checking the interpretedResults object since it is complex
|
||||
});
|
||||
publisher.resetHistory();
|
||||
publisher.mockClear();
|
||||
|
||||
// now, let's try to download it again. This time, since it's already in memory,
|
||||
// it should not even be re-published
|
||||
await arm.downloadAnalysisResults(analysisSummary, publisher);
|
||||
expect(publisher).to.not.have.been.called;
|
||||
expect(publisher).not.toBeCalled();
|
||||
});
|
||||
|
||||
it("should download two artifacts at once", async () => {
|
||||
const publisher = sandbox.spy();
|
||||
const publisher = jest.fn();
|
||||
const analysisSummaries = [
|
||||
remoteQueryResult0.analysisSummaries[0],
|
||||
remoteQueryResult0.analysisSummaries[1],
|
||||
];
|
||||
await arm.loadAnalysesResults(analysisSummaries, undefined, publisher);
|
||||
|
||||
const trimmed = publisher
|
||||
.getCalls()
|
||||
.map((call) => call.args[0])
|
||||
const trimmed = publisher.mock.calls
|
||||
.map((call) => call[0])
|
||||
.map((args) => {
|
||||
args.forEach(
|
||||
(analysisResult: any) => delete analysisResult.interpretedResults,
|
||||
@@ -333,7 +345,7 @@ describe("Remote queries and query history manager", function () {
|
||||
});
|
||||
|
||||
// As before, but now both summaries should have been published
|
||||
expect(trimmed[0]).to.deep.eq([
|
||||
expect(trimmed[0]).toEqual([
|
||||
{
|
||||
nwo: "github/vscode-codeql",
|
||||
status: "InProgress",
|
||||
@@ -343,7 +355,7 @@ describe("Remote queries and query history manager", function () {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(trimmed[1]).to.deep.eq([
|
||||
expect(trimmed[1]).toEqual([
|
||||
{
|
||||
nwo: "github/vscode-codeql",
|
||||
status: "InProgress",
|
||||
@@ -364,7 +376,7 @@ describe("Remote queries and query history manager", function () {
|
||||
// github/vscode-codeql is completed first or other/hucairz is.
|
||||
// There is not much point in trying to test it if the other calls are correct.
|
||||
|
||||
expect(trimmed[3]).to.deep.eq([
|
||||
expect(trimmed[3]).toEqual([
|
||||
{
|
||||
nwo: "github/vscode-codeql",
|
||||
status: "Completed",
|
||||
@@ -381,11 +393,11 @@ describe("Remote queries and query history manager", function () {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(publisher).to.have.callCount(4);
|
||||
expect(publisher).toBeCalledTimes(4);
|
||||
});
|
||||
|
||||
it("should avoid publishing when the request is cancelled", async () => {
|
||||
const publisher = sandbox.spy();
|
||||
const publisher = jest.fn();
|
||||
const analysisSummaries = [...remoteQueryResult0.analysisSummaries];
|
||||
|
||||
try {
|
||||
@@ -396,16 +408,16 @@ describe("Remote queries and query history manager", function () {
|
||||
} as CancellationToken,
|
||||
publisher,
|
||||
);
|
||||
expect.fail("Should have thrown");
|
||||
fail("Should have thrown");
|
||||
} catch (e) {
|
||||
expect(getErrorMessage(e)).to.contain("cancelled");
|
||||
expect(getErrorMessage(e)).toContain("cancelled");
|
||||
}
|
||||
|
||||
expect(publisher).not.to.have.been.called;
|
||||
expect(publisher).not.toBeCalled();
|
||||
});
|
||||
|
||||
it("should get the analysis results", async () => {
|
||||
const publisher = sandbox.spy();
|
||||
const publisher = jest.fn();
|
||||
const analysisSummaries0 = [
|
||||
remoteQueryResult0.analysisSummaries[0],
|
||||
remoteQueryResult0.analysisSummaries[1],
|
||||
@@ -419,18 +431,18 @@ describe("Remote queries and query history manager", function () {
|
||||
const result0Again = arm.getAnalysesResults(rawQueryHistory[0].queryId);
|
||||
|
||||
// Shoule be equal, but not equivalent
|
||||
expect(result0).to.deep.eq(result0Again);
|
||||
expect(result0).not.to.eq(result0Again);
|
||||
expect(result0).toEqual(result0Again);
|
||||
expect(result0).not.toBe(result0Again);
|
||||
|
||||
const result1 = arm.getAnalysesResults(rawQueryHistory[1].queryId);
|
||||
const result1Again = arm.getAnalysesResults(rawQueryHistory[1].queryId);
|
||||
expect(result1).to.deep.eq(result1Again);
|
||||
expect(result1).not.to.eq(result1Again);
|
||||
expect(result1).toEqual(result1Again);
|
||||
expect(result1).not.toBe(result1Again);
|
||||
});
|
||||
|
||||
// This test is failing on windows in CI.
|
||||
it.skip("should read sarif", async () => {
|
||||
const publisher = sandbox.spy();
|
||||
const publisher = jest.fn();
|
||||
const analysisSummaries0 = [remoteQueryResult0.analysisSummaries[0]];
|
||||
await arm.loadAnalysesResults(analysisSummaries0, undefined, publisher);
|
||||
|
||||
@@ -447,7 +459,11 @@ describe("Remote queries and query history manager", function () {
|
||||
.flatMap((run: any) => run.results)
|
||||
.map((result: any) => ({ message: result.message.text }));
|
||||
|
||||
expect(publisher.getCall(1).args[0][0].results).to.deep.eq(queryResults);
|
||||
expect(publisher).toHaveBeenNthCalledWith(2, [
|
||||
{
|
||||
results: queryResults,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("should check if an artifact is downloaded and not in memory", async () => {
|
||||
@@ -462,21 +478,21 @@ describe("Remote queries and query history manager", function () {
|
||||
await (arm as any).isAnalysisDownloaded(
|
||||
remoteQueryResult0.analysisSummaries[0],
|
||||
),
|
||||
).to.be.true;
|
||||
).toBe(true);
|
||||
|
||||
// in memory
|
||||
expect(
|
||||
await (arm as any).isAnalysisDownloaded(
|
||||
remoteQueryResult0.analysisSummaries[1],
|
||||
),
|
||||
).to.be.true;
|
||||
).toBe(true);
|
||||
|
||||
// not downloaded
|
||||
expect(
|
||||
await (arm as any).isAnalysisDownloaded(
|
||||
remoteQueryResult0.analysisSummaries[2],
|
||||
),
|
||||
).to.be.false;
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("should load downloaded artifacts", async () => {
|
||||
@@ -486,9 +502,9 @@ describe("Remote queries and query history manager", function () {
|
||||
.getAnalysesResults(queryId)
|
||||
.map((ar) => ar.nwo)
|
||||
.sort();
|
||||
expect(analysesResultsNwos[0]).to.eq("github/vscode-codeql");
|
||||
expect(analysesResultsNwos[1]).to.eq("other/hucairz");
|
||||
expect(analysesResultsNwos.length).to.eq(2);
|
||||
expect(analysesResultsNwos[0]).toBe("github/vscode-codeql");
|
||||
expect(analysesResultsNwos[1]).toBe("other/hucairz");
|
||||
expect(analysesResultsNwos.length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,96 +1,82 @@
|
||||
import * as sinon from "sinon";
|
||||
import { expect } from "chai";
|
||||
import { window } from "vscode";
|
||||
import * as pq from "proxyquire";
|
||||
import { QuickPickItem, window } from "vscode";
|
||||
import * as fs from "fs-extra";
|
||||
import { UserCancellationException } from "../../../commandRunner";
|
||||
|
||||
const proxyquire = pq.noPreserveCache();
|
||||
import * as config from "../../../config";
|
||||
import { getRepositorySelection } from "../../../remote-queries/repository-selection";
|
||||
|
||||
describe("repository selection", async () => {
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
describe("repository selection", () => {
|
||||
const quickPickSpy = jest.spyOn(window, "showQuickPick");
|
||||
const showInputBoxSpy = jest.spyOn(window, "showInputBox");
|
||||
|
||||
let quickPickSpy: sinon.SinonStub;
|
||||
let showInputBoxSpy: sinon.SinonStub;
|
||||
const getRemoteRepositoryListsSpy = jest.spyOn(
|
||||
config,
|
||||
"getRemoteRepositoryLists",
|
||||
);
|
||||
const getRemoteRepositoryListsPathSpy = jest.spyOn(
|
||||
config,
|
||||
"getRemoteRepositoryListsPath",
|
||||
);
|
||||
|
||||
let getRemoteRepositoryListsSpy: sinon.SinonStub;
|
||||
let getRemoteRepositoryListsPathSpy: sinon.SinonStub;
|
||||
|
||||
let pathExistsStub: sinon.SinonStub;
|
||||
let fsStatStub: sinon.SinonStub;
|
||||
let fsReadFileStub: sinon.SinonStub;
|
||||
|
||||
let mod: any;
|
||||
const pathExistsStub = jest.spyOn(fs, "pathExists");
|
||||
const fsStatStub = jest.spyOn(fs, "stat");
|
||||
const fsReadFileStub = jest.spyOn(fs, "readFile");
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
quickPickSpy.mockReset().mockResolvedValue(undefined);
|
||||
showInputBoxSpy.mockReset().mockResolvedValue(undefined);
|
||||
|
||||
quickPickSpy = sandbox.stub(window, "showQuickPick");
|
||||
showInputBoxSpy = sandbox.stub(window, "showInputBox");
|
||||
getRemoteRepositoryListsSpy.mockReset().mockReturnValue(undefined);
|
||||
getRemoteRepositoryListsPathSpy.mockReset().mockReturnValue(undefined);
|
||||
|
||||
getRemoteRepositoryListsSpy = sandbox.stub();
|
||||
getRemoteRepositoryListsPathSpy = sandbox.stub();
|
||||
|
||||
pathExistsStub = sandbox.stub(fs, "pathExists");
|
||||
fsStatStub = sandbox.stub(fs, "stat");
|
||||
fsReadFileStub = sandbox.stub(fs, "readFile");
|
||||
|
||||
mod = proxyquire("../../../remote-queries/repository-selection", {
|
||||
"../config": {
|
||||
getRemoteRepositoryLists: getRemoteRepositoryListsSpy,
|
||||
getRemoteRepositoryListsPath: getRemoteRepositoryListsPathSpy,
|
||||
},
|
||||
"fs-extra": {
|
||||
pathExists: pathExistsStub,
|
||||
stat: fsStatStub,
|
||||
readFile: fsReadFileStub,
|
||||
},
|
||||
});
|
||||
pathExistsStub.mockReset().mockImplementation(() => false);
|
||||
fsStatStub.mockReset().mockRejectedValue(new Error("not found"));
|
||||
fsReadFileStub.mockReset().mockRejectedValue(new Error("not found"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("repo lists from settings", async () => {
|
||||
describe("repo lists from settings", () => {
|
||||
it("should allow selection from repo lists from your pre-defined config", async () => {
|
||||
// Fake return values
|
||||
quickPickSpy.resolves({ repositories: ["foo/bar", "foo/baz"] });
|
||||
getRemoteRepositoryListsSpy.returns({
|
||||
quickPickSpy.mockResolvedValue({
|
||||
repositories: ["foo/bar", "foo/baz"],
|
||||
} as unknown as QuickPickItem);
|
||||
getRemoteRepositoryListsSpy.mockReturnValue({
|
||||
list1: ["foo/bar", "foo/baz"],
|
||||
list2: [],
|
||||
});
|
||||
|
||||
// Make the function call
|
||||
const repoSelection = await mod.getRepositorySelection();
|
||||
const repoSelection = await getRepositorySelection();
|
||||
|
||||
// Check that the return value is correct
|
||||
expect(repoSelection.repositoryLists).to.be.undefined;
|
||||
expect(repoSelection.owners).to.be.undefined;
|
||||
expect(repoSelection.repositories).to.deep.eq(["foo/bar", "foo/baz"]);
|
||||
expect(repoSelection.repositoryLists).toBeUndefined();
|
||||
expect(repoSelection.owners).toBeUndefined();
|
||||
expect(repoSelection.repositories).toEqual(["foo/bar", "foo/baz"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("system level repo lists", async () => {
|
||||
describe("system level repo lists", () => {
|
||||
it("should allow selection from repo lists defined at the system level", async () => {
|
||||
// Fake return values
|
||||
quickPickSpy.resolves({ repositoryList: "top_100" });
|
||||
getRemoteRepositoryListsSpy.returns({
|
||||
quickPickSpy.mockResolvedValue({
|
||||
repositoryList: "top_100",
|
||||
} as unknown as QuickPickItem);
|
||||
getRemoteRepositoryListsSpy.mockReturnValue({
|
||||
list1: ["foo/bar", "foo/baz"],
|
||||
list2: [],
|
||||
});
|
||||
|
||||
// Make the function call
|
||||
const repoSelection = await mod.getRepositorySelection();
|
||||
const repoSelection = await getRepositorySelection();
|
||||
|
||||
// Check that the return value is correct
|
||||
expect(repoSelection.repositories).to.be.undefined;
|
||||
expect(repoSelection.owners).to.be.undefined;
|
||||
expect(repoSelection.repositoryLists).to.deep.eq(["top_100"]);
|
||||
expect(repoSelection.repositories).toBeUndefined();
|
||||
expect(repoSelection.owners).toBeUndefined();
|
||||
expect(repoSelection.repositoryLists).toEqual(["top_100"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("custom owner", async () => {
|
||||
describe("custom owner", () => {
|
||||
// Test the owner regex in various "good" cases
|
||||
const goodOwners = [
|
||||
"owner",
|
||||
@@ -102,17 +88,19 @@ describe("repository selection", async () => {
|
||||
goodOwners.forEach((owner) => {
|
||||
it(`should run on a valid owner that you enter in the text box: ${owner}`, async () => {
|
||||
// Fake return values
|
||||
quickPickSpy.resolves({ useAllReposOfOwner: true });
|
||||
getRemoteRepositoryListsSpy.returns({}); // no pre-defined repo lists
|
||||
showInputBoxSpy.resolves(owner);
|
||||
quickPickSpy.mockResolvedValue({
|
||||
useAllReposOfOwner: true,
|
||||
} as unknown as QuickPickItem);
|
||||
getRemoteRepositoryListsSpy.mockReturnValue({}); // no pre-defined repo lists
|
||||
showInputBoxSpy.mockResolvedValue(owner);
|
||||
|
||||
// Make the function call
|
||||
const repoSelection = await mod.getRepositorySelection();
|
||||
const repoSelection = await getRepositorySelection();
|
||||
|
||||
// Check that the return value is correct
|
||||
expect(repoSelection.repositories).to.be.undefined;
|
||||
expect(repoSelection.repositoryLists).to.be.undefined;
|
||||
expect(repoSelection.owners).to.deep.eq([owner]);
|
||||
expect(repoSelection.repositories).toBeUndefined();
|
||||
expect(repoSelection.repositoryLists).toBeUndefined();
|
||||
expect(repoSelection.owners).toEqual([owner]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -121,33 +109,38 @@ describe("repository selection", async () => {
|
||||
badOwners.forEach((owner) => {
|
||||
it(`should show an error message if you enter an invalid owner in the text box: ${owner}`, async () => {
|
||||
// Fake return values
|
||||
quickPickSpy.resolves({ useAllReposOfOwner: true });
|
||||
getRemoteRepositoryListsSpy.returns({}); // no pre-defined repo lists
|
||||
showInputBoxSpy.resolves(owner);
|
||||
quickPickSpy.mockResolvedValue({
|
||||
useAllReposOfOwner: true,
|
||||
} as unknown as QuickPickItem);
|
||||
getRemoteRepositoryListsSpy.mockReturnValue({}); // no pre-defined repo lists
|
||||
showInputBoxSpy.mockResolvedValue(owner);
|
||||
|
||||
// Function call should throw a UserCancellationException
|
||||
await expect(mod.getRepositorySelection()).to.be.rejectedWith(
|
||||
Error,
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
`Invalid user or organization: ${owner}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should be ok for the user to change their mind", async () => {
|
||||
quickPickSpy.resolves({ useAllReposOfOwner: true });
|
||||
getRemoteRepositoryListsSpy.returns({});
|
||||
quickPickSpy.mockResolvedValue({
|
||||
useAllReposOfOwner: true,
|
||||
} as unknown as QuickPickItem);
|
||||
getRemoteRepositoryListsSpy.mockReturnValue({});
|
||||
|
||||
// The user pressed escape to cancel the operation
|
||||
showInputBoxSpy.resolves(undefined);
|
||||
showInputBoxSpy.mockResolvedValue(undefined);
|
||||
|
||||
await expect(mod.getRepositorySelection()).to.be.rejectedWith(
|
||||
UserCancellationException,
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
"No repositories selected",
|
||||
);
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
UserCancellationException,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("custom repo", async () => {
|
||||
describe("custom repo", () => {
|
||||
// Test the repo regex in various "good" cases
|
||||
const goodRepos = [
|
||||
"owner/repo",
|
||||
@@ -157,17 +150,19 @@ describe("repository selection", async () => {
|
||||
goodRepos.forEach((repo) => {
|
||||
it(`should run on a valid repo that you enter in the text box: ${repo}`, async () => {
|
||||
// Fake return values
|
||||
quickPickSpy.resolves({ useCustomRepo: true });
|
||||
getRemoteRepositoryListsSpy.returns({}); // no pre-defined repo lists
|
||||
showInputBoxSpy.resolves(repo);
|
||||
quickPickSpy.mockResolvedValue({
|
||||
useCustomRepo: true,
|
||||
} as unknown as QuickPickItem);
|
||||
getRemoteRepositoryListsSpy.mockReturnValue({}); // no pre-defined repo lists
|
||||
showInputBoxSpy.mockResolvedValue(repo);
|
||||
|
||||
// Make the function call
|
||||
const repoSelection = await mod.getRepositorySelection();
|
||||
const repoSelection = await getRepositorySelection();
|
||||
|
||||
// Check that the return value is correct
|
||||
expect(repoSelection.repositoryLists).to.be.undefined;
|
||||
expect(repoSelection.owners).to.be.undefined;
|
||||
expect(repoSelection.repositories).to.deep.equal([repo]);
|
||||
expect(repoSelection.repositoryLists).toBeUndefined();
|
||||
expect(repoSelection.owners).toBeUndefined();
|
||||
expect(repoSelection.repositories).toEqual([repo]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -181,120 +176,129 @@ describe("repository selection", async () => {
|
||||
badRepos.forEach((repo) => {
|
||||
it(`should show an error message if you enter an invalid repo in the text box: ${repo}`, async () => {
|
||||
// Fake return values
|
||||
quickPickSpy.resolves({ useCustomRepo: true });
|
||||
getRemoteRepositoryListsSpy.returns({}); // no pre-defined repo lists
|
||||
showInputBoxSpy.resolves(repo);
|
||||
quickPickSpy.mockResolvedValue({
|
||||
useCustomRepo: true,
|
||||
} as unknown as QuickPickItem);
|
||||
getRemoteRepositoryListsSpy.mockReturnValue({}); // no pre-defined repo lists
|
||||
showInputBoxSpy.mockResolvedValue(repo);
|
||||
|
||||
// Function call should throw a UserCancellationException
|
||||
await expect(mod.getRepositorySelection()).to.be.rejectedWith(
|
||||
UserCancellationException,
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
"Invalid repository format",
|
||||
);
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
UserCancellationException,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should be ok for the user to change their mind", async () => {
|
||||
quickPickSpy.resolves({ useCustomRepo: true });
|
||||
getRemoteRepositoryListsSpy.returns({});
|
||||
quickPickSpy.mockResolvedValue({
|
||||
useCustomRepo: true,
|
||||
} as unknown as QuickPickItem);
|
||||
getRemoteRepositoryListsSpy.mockReturnValue({});
|
||||
|
||||
// The user pressed escape to cancel the operation
|
||||
showInputBoxSpy.resolves(undefined);
|
||||
showInputBoxSpy.mockResolvedValue(undefined);
|
||||
|
||||
await expect(mod.getRepositorySelection()).to.be.rejectedWith(
|
||||
UserCancellationException,
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
"No repositories selected",
|
||||
);
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
UserCancellationException,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("external repository lists file", async () => {
|
||||
describe("external repository lists file", () => {
|
||||
it("should fail if path does not exist", async () => {
|
||||
const fakeFilePath = "/path/that/does/not/exist.json";
|
||||
getRemoteRepositoryListsPathSpy.returns(fakeFilePath);
|
||||
pathExistsStub.resolves(false);
|
||||
getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath);
|
||||
pathExistsStub.mockImplementation(() => false);
|
||||
|
||||
await expect(mod.getRepositorySelection()).to.be.rejectedWith(
|
||||
Error,
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
`External repository lists file does not exist at ${fakeFilePath}`,
|
||||
);
|
||||
});
|
||||
|
||||
it("should fail if path points to directory", async () => {
|
||||
const fakeFilePath = "/path/to/dir";
|
||||
getRemoteRepositoryListsPathSpy.returns(fakeFilePath);
|
||||
pathExistsStub.resolves(true);
|
||||
fsStatStub.resolves({ isDirectory: () => true } as any);
|
||||
getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath);
|
||||
pathExistsStub.mockImplementation(() => true);
|
||||
fsStatStub.mockResolvedValue({ isDirectory: () => true } as any);
|
||||
|
||||
await expect(mod.getRepositorySelection()).to.be.rejectedWith(
|
||||
Error,
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
"External repository lists path should not point to a directory",
|
||||
);
|
||||
});
|
||||
|
||||
it("should fail if file does not have valid JSON", async () => {
|
||||
const fakeFilePath = "/path/to/file.json";
|
||||
getRemoteRepositoryListsPathSpy.returns(fakeFilePath);
|
||||
pathExistsStub.resolves(true);
|
||||
fsStatStub.resolves({ isDirectory: () => false } as any);
|
||||
fsReadFileStub.resolves("not-json" as any as Buffer);
|
||||
getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath);
|
||||
pathExistsStub.mockImplementation(() => true);
|
||||
fsStatStub.mockResolvedValue({ isDirectory: () => false } as any);
|
||||
fsReadFileStub.mockResolvedValue("not-json" as any as Buffer);
|
||||
|
||||
await expect(mod.getRepositorySelection()).to.be.rejectedWith(
|
||||
Error,
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
"Invalid repository lists file. It should contain valid JSON.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should fail if file contains array", async () => {
|
||||
const fakeFilePath = "/path/to/file.json";
|
||||
getRemoteRepositoryListsPathSpy.returns(fakeFilePath);
|
||||
pathExistsStub.resolves(true);
|
||||
fsStatStub.resolves({ isDirectory: () => false } as any);
|
||||
fsReadFileStub.resolves("[]" as any as Buffer);
|
||||
getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath);
|
||||
pathExistsStub.mockImplementation(() => true);
|
||||
fsStatStub.mockResolvedValue({ isDirectory: () => false } as any);
|
||||
fsReadFileStub.mockResolvedValue("[]" as any as Buffer);
|
||||
|
||||
await expect(mod.getRepositorySelection()).to.be.rejectedWith(
|
||||
Error,
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
"Invalid repository lists file. It should be an object mapping names to a list of repositories.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should fail if file does not contain repo lists in the right format", async () => {
|
||||
const fakeFilePath = "/path/to/file.json";
|
||||
getRemoteRepositoryListsPathSpy.returns(fakeFilePath);
|
||||
pathExistsStub.resolves(true);
|
||||
fsStatStub.resolves({ isDirectory: () => false } as any);
|
||||
getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath);
|
||||
pathExistsStub.mockImplementation(() => true);
|
||||
fsStatStub.mockResolvedValue({ isDirectory: () => false } as any);
|
||||
const repoLists = {
|
||||
list1: "owner1/repo1",
|
||||
};
|
||||
fsReadFileStub.resolves(JSON.stringify(repoLists) as any as Buffer);
|
||||
fsReadFileStub.mockResolvedValue(
|
||||
JSON.stringify(repoLists) as any as Buffer,
|
||||
);
|
||||
|
||||
await expect(mod.getRepositorySelection()).to.be.rejectedWith(
|
||||
Error,
|
||||
await expect(getRepositorySelection()).rejects.toThrow(
|
||||
"Invalid repository lists file. It should contain an array of repositories for each list.",
|
||||
);
|
||||
});
|
||||
|
||||
it("should get repo lists from file", async () => {
|
||||
const fakeFilePath = "/path/to/file.json";
|
||||
getRemoteRepositoryListsPathSpy.returns(fakeFilePath);
|
||||
pathExistsStub.resolves(true);
|
||||
fsStatStub.resolves({ isDirectory: () => false } as any);
|
||||
getRemoteRepositoryListsPathSpy.mockReturnValue(fakeFilePath);
|
||||
pathExistsStub.mockImplementation(() => true);
|
||||
fsStatStub.mockResolvedValue({ isDirectory: () => false } as any);
|
||||
const repoLists = {
|
||||
list1: ["owner1/repo1", "owner2/repo2"],
|
||||
list2: ["owner3/repo3"],
|
||||
};
|
||||
fsReadFileStub.resolves(JSON.stringify(repoLists) as any as Buffer);
|
||||
getRemoteRepositoryListsSpy.returns({
|
||||
fsReadFileStub.mockResolvedValue(
|
||||
JSON.stringify(repoLists) as any as Buffer,
|
||||
);
|
||||
getRemoteRepositoryListsSpy.mockReturnValue({
|
||||
list3: ["onwer4/repo4"],
|
||||
list4: [],
|
||||
});
|
||||
|
||||
quickPickSpy.resolves({ repositories: ["owner3/repo3"] });
|
||||
quickPickSpy.mockResolvedValue({
|
||||
repositories: ["owner3/repo3"],
|
||||
} as unknown as QuickPickItem);
|
||||
|
||||
const repoSelection = await mod.getRepositorySelection();
|
||||
const repoSelection = await getRepositorySelection();
|
||||
|
||||
expect(repoSelection.repositoryLists).to.be.undefined;
|
||||
expect(repoSelection.owners).to.be.undefined;
|
||||
expect(repoSelection.repositories).to.deep.eq(["owner3/repo3"]);
|
||||
expect(repoSelection.repositoryLists).toBeUndefined();
|
||||
expect(repoSelection.owners).toBeUndefined();
|
||||
expect(repoSelection.repositories).toEqual(["owner3/repo3"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import * as sinon from "sinon";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { ExtensionContext, Uri, window, workspace } from "vscode";
|
||||
import {
|
||||
ExtensionContext,
|
||||
TextDocument,
|
||||
TextEditor,
|
||||
Uri,
|
||||
window,
|
||||
workspace,
|
||||
} from "vscode";
|
||||
import { QueryHistoryConfig } from "../../../config";
|
||||
import { DatabaseManager } from "../../../databases";
|
||||
import { tmpDir } from "../../../helpers";
|
||||
@@ -22,7 +27,7 @@ import { VariantAnalysisManager } from "../../../remote-queries/variant-analysis
|
||||
* Tests for variant analyses and how they interact with the query history manager.
|
||||
*/
|
||||
|
||||
describe("Variant Analyses and QueryHistoryManager", function () {
|
||||
describe("Variant Analyses and QueryHistoryManager", () => {
|
||||
const EXTENSION_PATH = path.join(__dirname, "../../../../");
|
||||
const STORAGE_DIR = Uri.file(
|
||||
path.join(tmpDir.name, "variant-analysis"),
|
||||
@@ -31,55 +36,50 @@ describe("Variant Analyses and QueryHistoryManager", function () {
|
||||
/** noop */
|
||||
};
|
||||
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
let qhm: QueryHistoryManager;
|
||||
let localQueriesResultsViewStub: ResultsView;
|
||||
let remoteQueriesManagerStub: RemoteQueriesManager;
|
||||
let variantAnalysisManagerStub: VariantAnalysisManager;
|
||||
let rawQueryHistory: any;
|
||||
let disposables: DisposableBucket;
|
||||
let showTextDocumentSpy: sinon.SinonSpy;
|
||||
let openTextDocumentSpy: sinon.SinonSpy;
|
||||
|
||||
let rehydrateVariantAnalysisStub: sinon.SinonStub;
|
||||
let removeVariantAnalysisStub: sinon.SinonStub;
|
||||
let showViewStub: sinon.SinonStub;
|
||||
const rehydrateVariantAnalysisStub = jest.fn();
|
||||
const removeVariantAnalysisStub = jest.fn();
|
||||
const showViewStub = jest.fn();
|
||||
|
||||
beforeEach(async function () {
|
||||
const localQueriesResultsViewStub = {
|
||||
showResults: jest.fn(),
|
||||
} as any as ResultsView;
|
||||
const remoteQueriesManagerStub = {
|
||||
onRemoteQueryAdded: jest.fn(),
|
||||
onRemoteQueryRemoved: jest.fn(),
|
||||
onRemoteQueryStatusUpdate: jest.fn(),
|
||||
rehydrateRemoteQuery: jest.fn(),
|
||||
removeRemoteQuery: jest.fn(),
|
||||
openRemoteQueryResults: jest.fn(),
|
||||
} as any as RemoteQueriesManager;
|
||||
const variantAnalysisManagerStub = {
|
||||
onVariantAnalysisAdded: jest.fn(),
|
||||
onVariantAnalysisRemoved: jest.fn(),
|
||||
removeVariantAnalysis: removeVariantAnalysisStub,
|
||||
rehydrateVariantAnalysis: rehydrateVariantAnalysisStub,
|
||||
onVariantAnalysisStatusUpdated: jest.fn(),
|
||||
showView: showViewStub,
|
||||
} as any as VariantAnalysisManager;
|
||||
|
||||
const showTextDocumentSpy = jest.spyOn(window, "showTextDocument");
|
||||
const openTextDocumentSpy = jest.spyOn(workspace, "openTextDocument");
|
||||
|
||||
beforeEach(async () => {
|
||||
// set a higher timeout since recursive delete below may take a while, expecially on Windows.
|
||||
this.timeout(120000);
|
||||
jest.setTimeout(120000);
|
||||
|
||||
// Since these tests change the state of the query history manager, we need to copy the original
|
||||
// to a temporary folder where we can manipulate it for tests
|
||||
await copyHistoryState();
|
||||
|
||||
sandbox = sinon.createSandbox();
|
||||
disposables = new DisposableBucket();
|
||||
|
||||
localQueriesResultsViewStub = {
|
||||
showResults: sandbox.stub(),
|
||||
} as any as ResultsView;
|
||||
|
||||
rehydrateVariantAnalysisStub = sandbox.stub();
|
||||
removeVariantAnalysisStub = sandbox.stub();
|
||||
showViewStub = sandbox.stub();
|
||||
|
||||
remoteQueriesManagerStub = {
|
||||
onRemoteQueryAdded: sandbox.stub(),
|
||||
onRemoteQueryRemoved: sandbox.stub(),
|
||||
onRemoteQueryStatusUpdate: sandbox.stub(),
|
||||
rehydrateRemoteQuery: sandbox.stub(),
|
||||
openRemoteQueryResults: sandbox.stub(),
|
||||
} as any as RemoteQueriesManager;
|
||||
|
||||
variantAnalysisManagerStub = {
|
||||
onVariantAnalysisAdded: sandbox.stub(),
|
||||
onVariantAnalysisRemoved: sandbox.stub(),
|
||||
removeVariantAnalysis: removeVariantAnalysisStub,
|
||||
rehydrateVariantAnalysis: rehydrateVariantAnalysisStub,
|
||||
onVariantAnalysisStatusUpdated: sandbox.stub(),
|
||||
showView: showViewStub,
|
||||
} as any as VariantAnalysisManager;
|
||||
rehydrateVariantAnalysisStub.mockReset();
|
||||
removeVariantAnalysisStub.mockReset();
|
||||
showViewStub.mockReset();
|
||||
|
||||
rawQueryHistory = fs.readJSONSync(
|
||||
path.join(STORAGE_DIR, "workspace-query-history.json"),
|
||||
@@ -105,30 +105,31 @@ describe("Variant Analyses and QueryHistoryManager", function () {
|
||||
);
|
||||
disposables.push(qhm);
|
||||
|
||||
showTextDocumentSpy = sandbox.spy(window, "showTextDocument");
|
||||
openTextDocumentSpy = sandbox.spy(workspace, "openTextDocument");
|
||||
showTextDocumentSpy.mockResolvedValue(undefined as unknown as TextEditor);
|
||||
openTextDocumentSpy.mockResolvedValue(undefined as unknown as TextDocument);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
afterEach(() => {
|
||||
deleteHistoryState();
|
||||
disposables.dispose(testDisposeHandler);
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it("should read query history that has variant analysis history items", async () => {
|
||||
await qhm.readQueryHistory();
|
||||
|
||||
expect(rehydrateVariantAnalysisStub).to.have.callCount(2);
|
||||
expect(rehydrateVariantAnalysisStub.getCall(0).args[0]).to.deep.eq(
|
||||
expect(rehydrateVariantAnalysisStub).toBeCalledTimes(2);
|
||||
expect(rehydrateVariantAnalysisStub).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
rawQueryHistory[0].variantAnalysis,
|
||||
);
|
||||
expect(rehydrateVariantAnalysisStub.getCall(1).args[0]).to.deep.eq(
|
||||
expect(rehydrateVariantAnalysisStub).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
rawQueryHistory[1].variantAnalysis,
|
||||
);
|
||||
|
||||
expect(qhm.treeDataProvider.allHistory[0]).to.deep.eq(rawQueryHistory[0]);
|
||||
expect(qhm.treeDataProvider.allHistory[1]).to.deep.eq(rawQueryHistory[1]);
|
||||
expect(qhm.treeDataProvider.allHistory.length).to.eq(2);
|
||||
expect(qhm.treeDataProvider.allHistory[0]).toEqual(rawQueryHistory[0]);
|
||||
expect(qhm.treeDataProvider.allHistory[1]).toEqual(rawQueryHistory[1]);
|
||||
expect(qhm.treeDataProvider.allHistory.length).toBe(2);
|
||||
});
|
||||
|
||||
it("should remove the variant analysis history item", async () => {
|
||||
@@ -139,9 +140,9 @@ describe("Variant Analyses and QueryHistoryManager", function () {
|
||||
|
||||
// Add it back to the history
|
||||
qhm.addQuery(rawQueryHistory[0]);
|
||||
expect(removeVariantAnalysisStub).to.have.callCount(1);
|
||||
expect(rehydrateVariantAnalysisStub).to.have.callCount(2);
|
||||
expect(qhm.treeDataProvider.allHistory).to.deep.eq([
|
||||
expect(removeVariantAnalysisStub).toBeCalledTimes(1);
|
||||
expect(rehydrateVariantAnalysisStub).toBeCalledTimes(2);
|
||||
expect(qhm.treeDataProvider.allHistory).toEqual([
|
||||
rawQueryHistory[1],
|
||||
rawQueryHistory[0],
|
||||
]);
|
||||
@@ -157,19 +158,21 @@ describe("Variant Analyses and QueryHistoryManager", function () {
|
||||
qhm.treeDataProvider.allHistory[0],
|
||||
]);
|
||||
|
||||
expect(removeVariantAnalysisStub.callCount).to.eq(2);
|
||||
expect(removeVariantAnalysisStub.getCall(0).args[0]).to.deep.eq(
|
||||
expect(removeVariantAnalysisStub).toHaveBeenCalledTimes(2);
|
||||
expect(removeVariantAnalysisStub).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
rawQueryHistory[1].variantAnalysis,
|
||||
);
|
||||
expect(removeVariantAnalysisStub.getCall(1).args[0]).to.deep.eq(
|
||||
expect(removeVariantAnalysisStub).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
rawQueryHistory[0].variantAnalysis,
|
||||
);
|
||||
expect(qhm.treeDataProvider.allHistory).to.deep.eq([]);
|
||||
expect(qhm.treeDataProvider.allHistory).toEqual([]);
|
||||
|
||||
// also, both queries should be removed from disk storage
|
||||
expect(
|
||||
fs.readJSONSync(path.join(STORAGE_DIR, "workspace-query-history.json")),
|
||||
).to.deep.eq({
|
||||
).toEqual({
|
||||
version: 2,
|
||||
queries: [],
|
||||
});
|
||||
@@ -179,23 +182,21 @@ describe("Variant Analyses and QueryHistoryManager", function () {
|
||||
await qhm.readQueryHistory();
|
||||
|
||||
await qhm.handleItemClicked(qhm.treeDataProvider.allHistory[0], []);
|
||||
expect(showViewStub).calledOnceWithExactly(
|
||||
rawQueryHistory[0].variantAnalysis.id,
|
||||
);
|
||||
expect(showViewStub).toBeCalledWith(rawQueryHistory[0].variantAnalysis.id);
|
||||
});
|
||||
|
||||
it("should get the query text", async () => {
|
||||
await qhm.readQueryHistory();
|
||||
await qhm.handleShowQueryText(qhm.treeDataProvider.allHistory[0], []);
|
||||
|
||||
expect(showTextDocumentSpy).to.have.been.calledOnce;
|
||||
expect(openTextDocumentSpy).to.have.been.calledOnce;
|
||||
expect(showTextDocumentSpy).toBeCalledTimes(1);
|
||||
expect(openTextDocumentSpy).toBeCalledTimes(1);
|
||||
|
||||
const uri: Uri = openTextDocumentSpy.getCall(0).args[0];
|
||||
expect(uri.scheme).to.eq("codeql");
|
||||
const uri: Uri = openTextDocumentSpy.mock.calls[0][0] as Uri;
|
||||
expect(uri.scheme).toBe("codeql");
|
||||
const params = new URLSearchParams(uri.query);
|
||||
expect(params.get("isQuickEval")).to.eq("false");
|
||||
expect(params.get("queryText")).to.eq(
|
||||
expect(params.get("isQuickEval")).toBe("false");
|
||||
expect(params.get("queryText")).toBe(
|
||||
rawQueryHistory[0].variantAnalysis.query.text,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { expect } from "chai";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import * as sinon from "sinon";
|
||||
import { Uri } from "vscode";
|
||||
|
||||
import {
|
||||
@@ -20,55 +18,56 @@ import { LegacyQueryRunner } from "../../legacy-query-server/legacyRunner";
|
||||
import { DatabaseItem } from "../../databases";
|
||||
|
||||
describe("run-queries", () => {
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
const isCanarySpy = jest.spyOn(config, "isCanary");
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
|
||||
sandbox.stub(config, "isCanary").returns(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
isCanarySpy.mockReset().mockReturnValue(false);
|
||||
});
|
||||
|
||||
it("should create a QueryEvaluationInfo", () => {
|
||||
const saveDir = "query-save-dir";
|
||||
const info = createMockQueryInfo(true, saveDir);
|
||||
|
||||
expect(info.compiledQueryPath).to.eq(
|
||||
expect(info.compiledQueryPath).toBe(
|
||||
path.join(saveDir, "compiledQuery.qlo"),
|
||||
);
|
||||
expect(info.queryEvalInfo.dilPath).to.eq(path.join(saveDir, "results.dil"));
|
||||
expect(info.queryEvalInfo.resultsPaths.resultsPath).to.eq(
|
||||
expect(info.queryEvalInfo.dilPath).toBe(path.join(saveDir, "results.dil"));
|
||||
expect(info.queryEvalInfo.resultsPaths.resultsPath).toBe(
|
||||
path.join(saveDir, "results.bqrs"),
|
||||
);
|
||||
expect(info.queryEvalInfo.resultsPaths.interpretedResultsPath).to.eq(
|
||||
expect(info.queryEvalInfo.resultsPaths.interpretedResultsPath).toBe(
|
||||
path.join(saveDir, "interpretedResults.sarif"),
|
||||
);
|
||||
expect(info.dbItemPath).to.eq(Uri.file("/abc").fsPath);
|
||||
expect(info.dbItemPath).toBe(Uri.file("/abc").fsPath);
|
||||
});
|
||||
|
||||
it("should check if interpreted results can be created", async () => {
|
||||
const info = createMockQueryInfo(true);
|
||||
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults(), "1").to.eq(true);
|
||||
// "1"
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults()).toBe(true);
|
||||
|
||||
(info.queryEvalInfo as any).databaseHasMetadataFile = false;
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults(), "2").to.eq(false);
|
||||
// "2"
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults()).toBe(false);
|
||||
|
||||
(info.queryEvalInfo as any).databaseHasMetadataFile = true;
|
||||
info.metadata!.kind = undefined;
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults(), "3").to.eq(false);
|
||||
// "3"
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults()).toBe(false);
|
||||
|
||||
info.metadata!.kind = "table";
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults(), "4").to.eq(false);
|
||||
// "4"
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults()).toBe(false);
|
||||
|
||||
// Graphs are not interpreted unless canary is set
|
||||
info.metadata!.kind = "graph";
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults(), "5").to.eq(false);
|
||||
// "5"
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults()).toBe(false);
|
||||
|
||||
(config.isCanary as sinon.SinonStub).returns(true);
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults(), "6").to.eq(true);
|
||||
isCanarySpy.mockReturnValueOnce(true);
|
||||
// "6"
|
||||
expect(info.queryEvalInfo.canHaveInterpretedResults()).toBe(true);
|
||||
});
|
||||
|
||||
[SELECT_QUERY_NAME, "other"].forEach((resultSetName) => {
|
||||
@@ -106,16 +105,17 @@ describe("run-queries", () => {
|
||||
);
|
||||
|
||||
const result = await promise;
|
||||
expect(result).to.eq(true);
|
||||
expect(result).toBe(true);
|
||||
|
||||
const csv = fs.readFileSync(csvLocation, "utf8");
|
||||
expect(csv).to.eq('a,"b"\nc,"d"\n"a",b,c\n');
|
||||
expect(csv).toBe('a,"b"\nc,"d"\n"a",b,c\n');
|
||||
|
||||
// now verify that we are using the expected result set
|
||||
expect((cliServer.bqrsDecode as sinon.SinonStub).callCount).to.eq(2);
|
||||
expect(
|
||||
(cliServer.bqrsDecode as sinon.SinonStub).getCall(0).args[1],
|
||||
).to.eq(resultSetName);
|
||||
expect(cliServer.bqrsDecode).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
resultSetName,
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -145,17 +145,18 @@ describe("run-queries", () => {
|
||||
const promise = info.queryEvalInfo.exportCsvResults(cliServer, csvLocation);
|
||||
|
||||
const result = await promise;
|
||||
expect(result).to.eq(true);
|
||||
expect(result).toBe(true);
|
||||
|
||||
const csv = fs.readFileSync(csvLocation, "utf8");
|
||||
expect(csv).to.eq(
|
||||
expect(csv).toBe(
|
||||
'"a","""b"""\nc,xxx,"d,yyy"\naaa " bbb,"ccc "" ddd"\ntrue,"false"\n123,"456"\n123.98,"456.99"\n',
|
||||
);
|
||||
|
||||
// now verify that we are using the expected result set
|
||||
expect((cliServer.bqrsDecode as sinon.SinonStub).callCount).to.eq(1);
|
||||
expect((cliServer.bqrsDecode as sinon.SinonStub).getCall(0).args[1]).to.eq(
|
||||
expect(cliServer.bqrsDecode).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
SELECT_QUERY_NAME,
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -169,7 +170,7 @@ describe("run-queries", () => {
|
||||
cliServer,
|
||||
csvLocation,
|
||||
);
|
||||
expect(result).to.eq(false);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
describe("compile", () => {
|
||||
@@ -191,11 +192,10 @@ describe("run-queries", () => {
|
||||
mockCancel as any,
|
||||
);
|
||||
|
||||
expect(results).to.deep.eq([
|
||||
{ message: "err", severity: Severity.ERROR },
|
||||
]);
|
||||
expect(results).toEqual([{ message: "err", severity: Severity.ERROR }]);
|
||||
|
||||
expect(qs.sendRequest).to.have.been.calledOnceWith(
|
||||
expect(qs.sendRequest).toHaveBeenCalledTimes(1);
|
||||
expect(qs.sendRequest).toHaveBeenCalledWith(
|
||||
compileQuery,
|
||||
{
|
||||
compilationOptions: {
|
||||
@@ -246,7 +246,8 @@ describe("run-queries", () => {
|
||||
dbItem,
|
||||
);
|
||||
|
||||
expect(qs.sendRequest).to.have.been.calledOnceWith(
|
||||
expect(qs.sendRequest).toHaveBeenCalledTimes(1);
|
||||
expect(qs.sendRequest).toHaveBeenCalledWith(
|
||||
registerDatabases,
|
||||
{
|
||||
databases: [
|
||||
@@ -284,7 +285,8 @@ describe("run-queries", () => {
|
||||
dbItem,
|
||||
);
|
||||
|
||||
expect(qs.sendRequest).to.have.been.calledOnceWith(
|
||||
expect(qs.sendRequest).toHaveBeenCalledTimes(1);
|
||||
expect(qs.sendRequest).toHaveBeenCalledWith(
|
||||
deregisterDatabases,
|
||||
{
|
||||
databases: [
|
||||
@@ -320,7 +322,7 @@ describe("run-queries", () => {
|
||||
mockCancel as any,
|
||||
dbItem,
|
||||
);
|
||||
expect(qs.sendRequest).not.to.have.been.called;
|
||||
expect(qs.sendRequest).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not deregister if unsupported", async () => {
|
||||
@@ -344,7 +346,7 @@ describe("run-queries", () => {
|
||||
mockCancel as any,
|
||||
dbItem,
|
||||
);
|
||||
expect(qs.sendRequest).not.to.have.been.called;
|
||||
expect(qs.sendRequest).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -372,18 +374,14 @@ describe("run-queries", () => {
|
||||
config: {
|
||||
timeoutSecs: 5,
|
||||
},
|
||||
sendRequest: sandbox.stub().returns(
|
||||
new Promise((resolve) => {
|
||||
resolve({
|
||||
messages: [
|
||||
{ message: "err", severity: Severity.ERROR },
|
||||
{ message: "warn", severity: Severity.WARNING },
|
||||
],
|
||||
});
|
||||
}),
|
||||
),
|
||||
sendRequest: jest.fn().mockResolvedValue({
|
||||
messages: [
|
||||
{ message: "err", severity: Severity.ERROR },
|
||||
{ message: "warn", severity: Severity.WARNING },
|
||||
],
|
||||
}),
|
||||
logger: {
|
||||
log: sandbox.spy(),
|
||||
log: jest.fn(),
|
||||
},
|
||||
cliServer,
|
||||
} as unknown as QueryServerClient;
|
||||
@@ -394,9 +392,9 @@ describe("run-queries", () => {
|
||||
): CodeQLCliServer {
|
||||
const mockServer: Record<string, any> = {};
|
||||
for (const [operation, returns] of Object.entries(mockOperations)) {
|
||||
mockServer[operation] = sandbox.stub();
|
||||
returns.forEach((returnValue, i) => {
|
||||
mockServer[operation].onCall(i).resolves(returnValue);
|
||||
mockServer[operation] = jest.fn();
|
||||
returns.forEach((returnValue) => {
|
||||
mockServer[operation].mockResolvedValueOnce(returnValue);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,22 @@
|
||||
import * as path from "path";
|
||||
import * as chai from "chai";
|
||||
import * as chaiAsPromised from "chai-as-promised";
|
||||
|
||||
import { sarifParser } from "../../sarif-parser";
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
const expect = chai.expect;
|
||||
|
||||
describe("sarif parser", function () {
|
||||
describe("sarif parser", () => {
|
||||
const sarifDir = path.join(__dirname, "data/sarif");
|
||||
it("should parse a valid SARIF file", async () => {
|
||||
const result = await sarifParser(path.join(sarifDir, "validSarif.sarif"));
|
||||
expect(result.version).to.exist;
|
||||
expect(result.runs).to.exist;
|
||||
expect(result.runs[0].tool).to.exist;
|
||||
expect(result.runs[0].tool.driver).to.exist;
|
||||
expect(result.runs.length).to.be.at.least(1);
|
||||
expect(result.version).toBeDefined();
|
||||
expect(result.runs).toBeDefined();
|
||||
expect(result.runs[0].tool).toBeDefined();
|
||||
expect(result.runs[0].tool.driver).toBeDefined();
|
||||
expect(result.runs.length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it("should return an empty array if there are no results", async () => {
|
||||
const result = await sarifParser(
|
||||
path.join(sarifDir, "emptyResultsSarif.sarif"),
|
||||
);
|
||||
expect(result.runs[0].results).to.be.empty;
|
||||
expect(result.runs[0].results).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { expect } from "chai";
|
||||
import * as sinon from "sinon";
|
||||
import TelemetryReporter from "vscode-extension-telemetry";
|
||||
import {
|
||||
ExtensionContext,
|
||||
@@ -16,12 +14,10 @@ import { fail } from "assert";
|
||||
import { ENABLE_TELEMETRY } from "../../config";
|
||||
import { createMockExtensionContext } from "./index";
|
||||
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
describe("telemetry reporting", function () {
|
||||
describe("telemetry reporting", () => {
|
||||
// setting preferences can trigger lots of background activity
|
||||
// so need to bump up the timeout of this test.
|
||||
this.timeout(10000);
|
||||
jest.setTimeout(10000);
|
||||
|
||||
let originalTelemetryExtension: boolean | undefined;
|
||||
let originalTelemetryGlobal: boolean | undefined;
|
||||
@@ -29,6 +25,21 @@ describe("telemetry reporting", function () {
|
||||
let ctx: ExtensionContext;
|
||||
let telemetryListener: TelemetryListener;
|
||||
|
||||
const sendTelemetryEventSpy = jest.spyOn(
|
||||
TelemetryReporter.prototype,
|
||||
"sendTelemetryEvent",
|
||||
);
|
||||
const sendTelemetryExceptionSpy = jest.spyOn(
|
||||
TelemetryReporter.prototype,
|
||||
"sendTelemetryException",
|
||||
);
|
||||
const disposeSpy = jest.spyOn(TelemetryReporter.prototype, "dispose");
|
||||
|
||||
const showInformationMessageSpy = jest.spyOn(
|
||||
window,
|
||||
"showInformationMessage",
|
||||
);
|
||||
|
||||
beforeEach(async () => {
|
||||
try {
|
||||
// in case a previous test has accidentally activated this extension,
|
||||
@@ -39,9 +50,11 @@ describe("telemetry reporting", function () {
|
||||
|
||||
ctx = createMockExtensionContext();
|
||||
|
||||
sandbox.stub(TelemetryReporter.prototype, "sendTelemetryEvent");
|
||||
sandbox.stub(TelemetryReporter.prototype, "sendTelemetryException");
|
||||
sandbox.stub(TelemetryReporter.prototype, "dispose");
|
||||
sendTelemetryEventSpy.mockReset().mockReturnValue(undefined);
|
||||
sendTelemetryExceptionSpy.mockReset().mockReturnValue(undefined);
|
||||
disposeSpy.mockReset().mockResolvedValue(undefined);
|
||||
|
||||
showInformationMessageSpy.mockReset().mockResolvedValue(undefined);
|
||||
|
||||
originalTelemetryExtension = workspace
|
||||
.getConfiguration()
|
||||
@@ -73,7 +86,6 @@ describe("telemetry reporting", function () {
|
||||
telemetryListener?.dispose();
|
||||
// await wait(100);
|
||||
try {
|
||||
sandbox.restore();
|
||||
await enableTelemetry("telemetry", originalTelemetryGlobal);
|
||||
await enableTelemetry("codeQL.telemetry", originalTelemetryExtension);
|
||||
} catch (e) {
|
||||
@@ -84,22 +96,22 @@ describe("telemetry reporting", function () {
|
||||
it("should initialize telemetry when both options are enabled", async () => {
|
||||
await telemetryListener.initialize();
|
||||
|
||||
expect(telemetryListener._reporter).not.to.be.undefined;
|
||||
expect(telemetryListener._reporter).toBeDefined();
|
||||
|
||||
const reporter: any = telemetryListener._reporter;
|
||||
expect(reporter.extensionId).to.eq("my-id");
|
||||
expect(reporter.extensionVersion).to.eq("1.2.3");
|
||||
expect(reporter.userOptIn).to.eq(true); // enabled
|
||||
expect(reporter.extensionId).toBe("my-id");
|
||||
expect(reporter.extensionVersion).toBe("1.2.3");
|
||||
expect(reporter.userOptIn).toBe(true); // enabled
|
||||
});
|
||||
|
||||
it("should initialize telemetry when global option disabled", async () => {
|
||||
try {
|
||||
await enableTelemetry("telemetry", false);
|
||||
await telemetryListener.initialize();
|
||||
expect(telemetryListener._reporter).not.to.be.undefined;
|
||||
expect(telemetryListener._reporter).toBeDefined();
|
||||
|
||||
const reporter: any = telemetryListener._reporter;
|
||||
expect(reporter.userOptIn).to.eq(false); // disabled
|
||||
expect(reporter.userOptIn).toBe(false); // disabled
|
||||
} catch (e) {
|
||||
fail(e as Error);
|
||||
}
|
||||
@@ -110,7 +122,7 @@ describe("telemetry reporting", function () {
|
||||
await enableTelemetry("codeQL.telemetry", false);
|
||||
await telemetryListener.initialize();
|
||||
|
||||
expect(telemetryListener._reporter).to.be.undefined;
|
||||
expect(telemetryListener._reporter).toBeUndefined();
|
||||
} catch (e) {
|
||||
fail(e as Error);
|
||||
}
|
||||
@@ -120,52 +132,52 @@ describe("telemetry reporting", function () {
|
||||
await enableTelemetry("codeQL.telemetry", false);
|
||||
await enableTelemetry("telemetry", false);
|
||||
await telemetryListener.initialize();
|
||||
expect(telemetryListener._reporter).to.be.undefined;
|
||||
expect(telemetryListener._reporter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should dispose telemetry object when re-initializing and should not add multiple", async () => {
|
||||
await telemetryListener.initialize();
|
||||
expect(telemetryListener._reporter).not.to.be.undefined;
|
||||
expect(telemetryListener._reporter).toBeDefined();
|
||||
const firstReporter = telemetryListener._reporter;
|
||||
await telemetryListener.initialize();
|
||||
expect(telemetryListener._reporter).not.to.be.undefined;
|
||||
expect(telemetryListener._reporter).not.to.eq(firstReporter);
|
||||
expect(telemetryListener._reporter).toBeDefined();
|
||||
expect(telemetryListener._reporter).not.toBe(firstReporter);
|
||||
|
||||
expect(TelemetryReporter.prototype.dispose).to.have.been.calledOnce;
|
||||
expect(disposeSpy).toBeCalledTimes(1);
|
||||
|
||||
// initializing a third time continues to dispose
|
||||
await telemetryListener.initialize();
|
||||
expect(TelemetryReporter.prototype.dispose).to.have.been.calledTwice;
|
||||
expect(disposeSpy).toBeCalledTimes(2);
|
||||
});
|
||||
|
||||
it("should reinitialize reporter when extension setting changes", async () => {
|
||||
await telemetryListener.initialize();
|
||||
|
||||
expect(TelemetryReporter.prototype.dispose).not.to.have.been.called;
|
||||
expect(telemetryListener._reporter).not.to.be.undefined;
|
||||
expect(disposeSpy).not.toBeCalled();
|
||||
expect(telemetryListener._reporter).toBeDefined();
|
||||
|
||||
// this disables the reporter
|
||||
await enableTelemetry("codeQL.telemetry", false);
|
||||
|
||||
expect(telemetryListener._reporter).to.be.undefined;
|
||||
expect(telemetryListener._reporter).toBeUndefined();
|
||||
|
||||
expect(TelemetryReporter.prototype.dispose).to.have.been.calledOnce;
|
||||
expect(disposeSpy).toBeCalledTimes(1);
|
||||
|
||||
// creates a new reporter, but does not dispose again
|
||||
await enableTelemetry("codeQL.telemetry", true);
|
||||
|
||||
expect(telemetryListener._reporter).not.to.be.undefined;
|
||||
expect(TelemetryReporter.prototype.dispose).to.have.been.calledOnce;
|
||||
expect(telemetryListener._reporter).toBeDefined();
|
||||
expect(disposeSpy).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should set userOprIn to false when global setting changes", async () => {
|
||||
await telemetryListener.initialize();
|
||||
|
||||
const reporter: any = telemetryListener._reporter;
|
||||
expect(reporter.userOptIn).to.eq(true); // enabled
|
||||
expect(reporter.userOptIn).toBe(true); // enabled
|
||||
|
||||
await enableTelemetry("telemetry", false);
|
||||
expect(reporter.userOptIn).to.eq(false); // disabled
|
||||
expect(reporter.userOptIn).toBe(false); // disabled
|
||||
});
|
||||
|
||||
it("should send an event", async () => {
|
||||
@@ -173,9 +185,7 @@ describe("telemetry reporting", function () {
|
||||
|
||||
telemetryListener.sendCommandUsage("command-id", 1234, undefined);
|
||||
|
||||
expect(
|
||||
TelemetryReporter.prototype.sendTelemetryEvent,
|
||||
).to.have.been.calledOnceWith(
|
||||
expect(sendTelemetryEventSpy).toHaveBeenCalledWith(
|
||||
"command-usage",
|
||||
{
|
||||
name: "command-id",
|
||||
@@ -185,8 +195,7 @@ describe("telemetry reporting", function () {
|
||||
{ executionTime: 1234 },
|
||||
);
|
||||
|
||||
expect(TelemetryReporter.prototype.sendTelemetryException).not.to.have.been
|
||||
.called;
|
||||
expect(sendTelemetryExceptionSpy).not.toBeCalled();
|
||||
});
|
||||
|
||||
it("should send a command usage event with an error", async () => {
|
||||
@@ -198,9 +207,7 @@ describe("telemetry reporting", function () {
|
||||
new UserCancellationException(),
|
||||
);
|
||||
|
||||
expect(
|
||||
TelemetryReporter.prototype.sendTelemetryEvent,
|
||||
).to.have.been.calledOnceWith(
|
||||
expect(sendTelemetryEventSpy).toHaveBeenCalledWith(
|
||||
"command-usage",
|
||||
{
|
||||
name: "command-id",
|
||||
@@ -210,8 +217,7 @@ describe("telemetry reporting", function () {
|
||||
{ executionTime: 1234 },
|
||||
);
|
||||
|
||||
expect(TelemetryReporter.prototype.sendTelemetryException).not.to.have.been
|
||||
.called;
|
||||
expect(sendTelemetryExceptionSpy).not.toBeCalled();
|
||||
});
|
||||
|
||||
it("should avoid sending an event when telemetry is disabled", async () => {
|
||||
@@ -221,10 +227,8 @@ describe("telemetry reporting", function () {
|
||||
telemetryListener.sendCommandUsage("command-id", 1234, undefined);
|
||||
telemetryListener.sendCommandUsage("command-id", 1234, new Error());
|
||||
|
||||
expect(TelemetryReporter.prototype.sendTelemetryEvent).not.to.have.been
|
||||
.called;
|
||||
expect(TelemetryReporter.prototype.sendTelemetryException).not.to.have.been
|
||||
.called;
|
||||
expect(sendTelemetryEventSpy).not.toBeCalled();
|
||||
expect(sendTelemetryExceptionSpy).not.toBeCalled();
|
||||
});
|
||||
|
||||
it("should send an event when telemetry is re-enabled", async () => {
|
||||
@@ -234,9 +238,7 @@ describe("telemetry reporting", function () {
|
||||
|
||||
telemetryListener.sendCommandUsage("command-id", 1234, undefined);
|
||||
|
||||
expect(
|
||||
TelemetryReporter.prototype.sendTelemetryEvent,
|
||||
).to.have.been.calledOnceWith(
|
||||
expect(sendTelemetryEventSpy).toHaveBeenCalledWith(
|
||||
"command-usage",
|
||||
{
|
||||
name: "command-id",
|
||||
@@ -267,8 +269,8 @@ describe("telemetry reporting", function () {
|
||||
},
|
||||
};
|
||||
const res = telemetryProcessor(envelop);
|
||||
expect(res).to.eq(true);
|
||||
expect(envelop).to.deep.eq({
|
||||
expect(res).toBe(true);
|
||||
expect(envelop).toEqual({
|
||||
tags: {
|
||||
other: true,
|
||||
},
|
||||
@@ -282,11 +284,16 @@ describe("telemetry reporting", function () {
|
||||
});
|
||||
});
|
||||
|
||||
it("should request permission if popup has never been seen before", async function () {
|
||||
this.timeout(3000);
|
||||
sandbox
|
||||
.stub(window, "showInformationMessage")
|
||||
.resolvesArg(3 /* "yes" item */);
|
||||
const resolveArg =
|
||||
(index: number) =>
|
||||
(...args: any[]) =>
|
||||
Promise.resolve(args[index]);
|
||||
|
||||
it("should request permission if popup has never been seen before", async () => {
|
||||
jest.setTimeout(3000);
|
||||
showInformationMessageSpy.mockImplementation(
|
||||
resolveArg(3 /* "yes" item */),
|
||||
);
|
||||
await ctx.globalState.update("telemetry-request-viewed", false);
|
||||
await enableTelemetry("codeQL.telemetry", false);
|
||||
|
||||
@@ -296,40 +303,36 @@ describe("telemetry reporting", function () {
|
||||
await wait(500);
|
||||
|
||||
// Dialog opened, user clicks "yes" and telemetry enabled
|
||||
expect(window.showInformationMessage).to.have.been.calledOnce;
|
||||
expect(ENABLE_TELEMETRY.getValue()).to.eq(true);
|
||||
expect(ctx.globalState.get("telemetry-request-viewed")).to.be.true;
|
||||
expect(showInformationMessageSpy).toBeCalledTimes(1);
|
||||
expect(ENABLE_TELEMETRY.getValue()).toBe(true);
|
||||
expect(ctx.globalState.get("telemetry-request-viewed")).toBe(true);
|
||||
});
|
||||
|
||||
it("should prevent telemetry if permission is denied", async () => {
|
||||
sandbox
|
||||
.stub(window, "showInformationMessage")
|
||||
.resolvesArg(4 /* "no" item */);
|
||||
showInformationMessageSpy.mockImplementation(resolveArg(4 /* "no" item */));
|
||||
await ctx.globalState.update("telemetry-request-viewed", false);
|
||||
await enableTelemetry("codeQL.telemetry", true);
|
||||
|
||||
await telemetryListener.initialize();
|
||||
|
||||
// Dialog opened, user clicks "no" and telemetry disabled
|
||||
expect(window.showInformationMessage).to.have.been.calledOnce;
|
||||
expect(ENABLE_TELEMETRY.getValue()).to.eq(false);
|
||||
expect(ctx.globalState.get("telemetry-request-viewed")).to.be.true;
|
||||
expect(showInformationMessageSpy).toBeCalledTimes(1);
|
||||
expect(ENABLE_TELEMETRY.getValue()).toBe(false);
|
||||
expect(ctx.globalState.get("telemetry-request-viewed")).toBe(true);
|
||||
});
|
||||
|
||||
it("should unchange telemetry if permission dialog is dismissed", async () => {
|
||||
sandbox
|
||||
.stub(window, "showInformationMessage")
|
||||
.resolves(undefined /* cancelled */);
|
||||
showInformationMessageSpy.mockResolvedValue(undefined /* cancelled */);
|
||||
await ctx.globalState.update("telemetry-request-viewed", false);
|
||||
|
||||
// this causes requestTelemetryPermission to be called
|
||||
await enableTelemetry("codeQL.telemetry", false);
|
||||
|
||||
// Dialog opened, and user closes without interacting with it
|
||||
expect(window.showInformationMessage).to.have.been.calledOnce;
|
||||
expect(ENABLE_TELEMETRY.getValue()).to.eq(false);
|
||||
expect(showInformationMessageSpy).toBeCalledTimes(1);
|
||||
expect(ENABLE_TELEMETRY.getValue()).toBe(false);
|
||||
// dialog was canceled, so should not have marked as viewed
|
||||
expect(ctx.globalState.get("telemetry-request-viewed")).to.be.false;
|
||||
expect(ctx.globalState.get("telemetry-request-viewed")).toBe(false);
|
||||
});
|
||||
|
||||
it("should unchange telemetry if permission dialog is cancelled if starting as true", async () => {
|
||||
@@ -337,9 +340,7 @@ describe("telemetry reporting", function () {
|
||||
|
||||
// as before, except start with telemetry enabled. It should _stay_ enabled if the
|
||||
// dialog is canceled.
|
||||
sandbox
|
||||
.stub(window, "showInformationMessage")
|
||||
.resolves(undefined /* cancelled */);
|
||||
showInformationMessageSpy.mockResolvedValue(undefined /* cancelled */);
|
||||
await ctx.globalState.update("telemetry-request-viewed", false);
|
||||
|
||||
// this causes requestTelemetryPermission to be called
|
||||
@@ -347,10 +348,10 @@ describe("telemetry reporting", function () {
|
||||
|
||||
// Dialog opened, and user closes without interacting with it
|
||||
// Telemetry state should not have changed
|
||||
expect(window.showInformationMessage).to.have.been.calledOnce;
|
||||
expect(ENABLE_TELEMETRY.getValue()).to.eq(true);
|
||||
expect(showInformationMessageSpy).toBeCalledTimes(1);
|
||||
expect(ENABLE_TELEMETRY.getValue()).toBe(true);
|
||||
// dialog was canceled, so should not have marked as viewed
|
||||
expect(ctx.globalState.get("telemetry-request-viewed")).to.be.false;
|
||||
expect(ctx.globalState.get("telemetry-request-viewed")).toBe(false);
|
||||
});
|
||||
|
||||
it("should avoid showing dialog if global telemetry is disabled", async () => {
|
||||
@@ -362,12 +363,11 @@ describe("telemetry reporting", function () {
|
||||
|
||||
await enableTelemetry("telemetry", false);
|
||||
await ctx.globalState.update("telemetry-request-viewed", false);
|
||||
sandbox.stub(window, "showInformationMessage");
|
||||
|
||||
await telemetryListener.initialize();
|
||||
|
||||
// popup should not be shown even though we have initialized telemetry
|
||||
expect(window.showInformationMessage).not.to.have.been.called;
|
||||
expect(showInformationMessageSpy).not.toBeCalled();
|
||||
});
|
||||
|
||||
// This test is failing because codeQL.canary is not a registered configuration.
|
||||
@@ -381,16 +381,14 @@ describe("telemetry reporting", function () {
|
||||
await enableTelemetry("codeQL.telemetry", false);
|
||||
await ctx.globalState.update("telemetry-request-viewed", true);
|
||||
await telemetryListener.initialize();
|
||||
sandbox
|
||||
.stub(window, "showInformationMessage")
|
||||
.resolves(undefined /* cancelled */);
|
||||
showInformationMessageSpy.mockResolvedValue(undefined /* cancelled */);
|
||||
|
||||
// set canary to true
|
||||
await workspace.getConfiguration().update("codeQL.canary", true);
|
||||
|
||||
// now, we should have to click through the telemetry requestor again
|
||||
expect(ctx.globalState.get("telemetry-request-viewed")).to.be.false;
|
||||
expect(window.showInformationMessage).to.have.been.calledOnce;
|
||||
expect(ctx.globalState.get("telemetry-request-viewed")).toBe(false);
|
||||
expect(showInformationMessageSpy).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
async function enableTelemetry(section: string, value: boolean | undefined) {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import * as sinon from "sinon";
|
||||
import * as fs from "fs-extra";
|
||||
import { Uri, WorkspaceFolder } from "vscode";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { QLTestAdapter } from "../../test-adapter";
|
||||
import { CodeQLCliServer } from "../../cli";
|
||||
@@ -17,14 +15,13 @@ describe("test-adapter", () => {
|
||||
let fakeDatabaseManager: DatabaseManager;
|
||||
let currentDatabaseItem: DatabaseItem | undefined;
|
||||
let databaseItems: DatabaseItem[] = [];
|
||||
let openDatabaseSpy: sinon.SinonStub;
|
||||
let removeDatabaseItemSpy: sinon.SinonStub;
|
||||
let renameDatabaseItemSpy: sinon.SinonStub;
|
||||
let setCurrentDatabaseItemSpy: sinon.SinonStub;
|
||||
let runTestsSpy: sinon.SinonStub;
|
||||
let resolveTestsSpy: sinon.SinonStub;
|
||||
let resolveQlpacksSpy: sinon.SinonStub;
|
||||
let sandox: sinon.SinonSandbox;
|
||||
const openDatabaseSpy = jest.fn();
|
||||
const removeDatabaseItemSpy = jest.fn();
|
||||
const renameDatabaseItemSpy = jest.fn();
|
||||
const setCurrentDatabaseItemSpy = jest.fn();
|
||||
const runTestsSpy = jest.fn();
|
||||
const resolveTestsSpy = jest.fn();
|
||||
const resolveQlpacksSpy = jest.fn();
|
||||
|
||||
const preTestDatabaseItem = new DatabaseItemImpl(
|
||||
Uri.file("/path/to/test/dir/dir.testproj"),
|
||||
@@ -44,27 +41,28 @@ describe("test-adapter", () => {
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
sandox = sinon.createSandbox();
|
||||
mockRunTests();
|
||||
openDatabaseSpy = sandox.stub().resolves(postTestDatabaseItem);
|
||||
removeDatabaseItemSpy = sandox.stub().resolves();
|
||||
renameDatabaseItemSpy = sandox.stub().resolves();
|
||||
setCurrentDatabaseItemSpy = sandox.stub().resolves();
|
||||
resolveQlpacksSpy = sandox.stub().resolves({});
|
||||
resolveTestsSpy = sandox.stub().resolves([]);
|
||||
openDatabaseSpy.mockReset().mockResolvedValue(postTestDatabaseItem);
|
||||
removeDatabaseItemSpy.mockReset().mockResolvedValue(undefined);
|
||||
renameDatabaseItemSpy.mockReset().mockResolvedValue(undefined);
|
||||
setCurrentDatabaseItemSpy.mockReset().mockResolvedValue(undefined);
|
||||
resolveQlpacksSpy.mockReset().mockResolvedValue({});
|
||||
resolveTestsSpy.mockReset().mockResolvedValue([]);
|
||||
fakeDatabaseManager = {
|
||||
currentDatabaseItem: undefined,
|
||||
databaseItems: undefined,
|
||||
openDatabase: openDatabaseSpy,
|
||||
removeDatabaseItem: removeDatabaseItemSpy,
|
||||
renameDatabaseItem: renameDatabaseItemSpy,
|
||||
setCurrentDatabaseItem: setCurrentDatabaseItemSpy,
|
||||
} as unknown as DatabaseManager;
|
||||
sandox
|
||||
.stub(fakeDatabaseManager, "currentDatabaseItem")
|
||||
.get(() => currentDatabaseItem);
|
||||
sandox.stub(fakeDatabaseManager, "databaseItems").get(() => databaseItems);
|
||||
sandox.stub(preTestDatabaseItem, "isAffectedByTest").resolves(true);
|
||||
Object.defineProperty(fakeDatabaseManager, "currentDatabaseItem", {
|
||||
get: () => currentDatabaseItem,
|
||||
});
|
||||
Object.defineProperty(fakeDatabaseManager, "databaseItems", {
|
||||
get: () => databaseItems,
|
||||
});
|
||||
|
||||
jest.spyOn(preTestDatabaseItem, "isAffectedByTest").mockResolvedValue(true);
|
||||
|
||||
adapter = new QLTestAdapter(
|
||||
{
|
||||
name: "ABC",
|
||||
@@ -79,12 +77,8 @@ describe("test-adapter", () => {
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandox.restore();
|
||||
});
|
||||
|
||||
it("should run some tests", async () => {
|
||||
const listenerSpy = sandox.spy();
|
||||
const listenerSpy = jest.fn();
|
||||
adapter.testStates(listenerSpy);
|
||||
const testsPath = Uri.parse("file:/ab/c").fsPath;
|
||||
const dPath = Uri.parse("file:/ab/c/d.ql").fsPath;
|
||||
@@ -93,72 +87,78 @@ describe("test-adapter", () => {
|
||||
|
||||
await adapter.run([testsPath]);
|
||||
|
||||
expect(listenerSpy.getCall(0).args).to.deep.eq([
|
||||
{ type: "started", tests: [testsPath] },
|
||||
]);
|
||||
expect(listenerSpy.getCall(1).args).to.deep.eq([
|
||||
{
|
||||
type: "test",
|
||||
state: "passed",
|
||||
test: dPath,
|
||||
message: undefined,
|
||||
decorations: [],
|
||||
},
|
||||
]);
|
||||
expect(listenerSpy.getCall(2).args).to.deep.eq([
|
||||
{
|
||||
type: "test",
|
||||
state: "errored",
|
||||
test: gPath,
|
||||
message: `\ncompilation error: ${gPath}\nERROR: abc\n`,
|
||||
decorations: [{ line: 1, message: "abc" }],
|
||||
},
|
||||
]);
|
||||
expect(listenerSpy.getCall(3).args).to.deep.eq([
|
||||
{
|
||||
type: "test",
|
||||
state: "failed",
|
||||
test: hPath,
|
||||
message: `\nfailed: ${hPath}\njkh\ntuv\n`,
|
||||
decorations: [],
|
||||
},
|
||||
]);
|
||||
expect(listenerSpy.getCall(4).args).to.deep.eq([{ type: "finished" }]);
|
||||
expect(listenerSpy).to.have.callCount(5);
|
||||
expect(listenerSpy).toBeCalledTimes(5);
|
||||
|
||||
expect(listenerSpy).toHaveBeenNthCalledWith(1, {
|
||||
type: "started",
|
||||
tests: [testsPath],
|
||||
});
|
||||
expect(listenerSpy).toHaveBeenNthCalledWith(2, {
|
||||
type: "test",
|
||||
state: "passed",
|
||||
test: dPath,
|
||||
message: undefined,
|
||||
decorations: [],
|
||||
});
|
||||
expect(listenerSpy).toHaveBeenNthCalledWith(3, {
|
||||
type: "test",
|
||||
state: "errored",
|
||||
test: gPath,
|
||||
message: `\ncompilation error: ${gPath}\nERROR: abc\n`,
|
||||
decorations: [{ line: 1, message: "abc" }],
|
||||
});
|
||||
expect(listenerSpy).toHaveBeenNthCalledWith(4, {
|
||||
type: "test",
|
||||
state: "failed",
|
||||
test: hPath,
|
||||
message: `\nfailed: ${hPath}\njkh\ntuv\n`,
|
||||
decorations: [],
|
||||
});
|
||||
expect(listenerSpy).toHaveBeenNthCalledWith(5, { type: "finished" });
|
||||
});
|
||||
|
||||
it("should reregister testproj databases around test run", async () => {
|
||||
sandox.stub(fs, "access").resolves();
|
||||
jest.spyOn(fs, "access").mockResolvedValue(undefined);
|
||||
|
||||
currentDatabaseItem = preTestDatabaseItem;
|
||||
databaseItems = [preTestDatabaseItem];
|
||||
await adapter.run(["/path/to/test/dir"]);
|
||||
|
||||
removeDatabaseItemSpy.getCall(0).calledBefore(runTestsSpy.getCall(0));
|
||||
openDatabaseSpy.getCall(0).calledAfter(runTestsSpy.getCall(0));
|
||||
renameDatabaseItemSpy.getCall(0).calledAfter(openDatabaseSpy.getCall(0));
|
||||
setCurrentDatabaseItemSpy
|
||||
.getCall(0)
|
||||
.calledAfter(openDatabaseSpy.getCall(0));
|
||||
expect(removeDatabaseItemSpy.mock.invocationCallOrder[0]).toBeGreaterThan(
|
||||
runTestsSpy.mock.invocationCallOrder[0],
|
||||
);
|
||||
expect(openDatabaseSpy.mock.invocationCallOrder[0]).toBeGreaterThan(
|
||||
runTestsSpy.mock.invocationCallOrder[0],
|
||||
);
|
||||
expect(renameDatabaseItemSpy.mock.invocationCallOrder[0]).toBeGreaterThan(
|
||||
openDatabaseSpy.mock.invocationCallOrder[0],
|
||||
);
|
||||
expect(
|
||||
setCurrentDatabaseItemSpy.mock.invocationCallOrder[0],
|
||||
).toBeGreaterThan(openDatabaseSpy.mock.invocationCallOrder[0]);
|
||||
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
removeDatabaseItemSpy,
|
||||
sinon.match.any,
|
||||
sinon.match.any,
|
||||
expect(removeDatabaseItemSpy).toBeCalledTimes(1);
|
||||
expect(removeDatabaseItemSpy).toBeCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
preTestDatabaseItem,
|
||||
);
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
openDatabaseSpy,
|
||||
sinon.match.any,
|
||||
sinon.match.any,
|
||||
|
||||
expect(openDatabaseSpy).toBeCalledTimes(1);
|
||||
expect(openDatabaseSpy).toBeCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
preTestDatabaseItem.databaseUri,
|
||||
);
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
renameDatabaseItemSpy,
|
||||
|
||||
expect(renameDatabaseItemSpy).toBeCalledTimes(1);
|
||||
expect(renameDatabaseItemSpy).toBeCalledWith(
|
||||
postTestDatabaseItem,
|
||||
preTestDatabaseItem.name,
|
||||
);
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
setCurrentDatabaseItemSpy,
|
||||
|
||||
expect(setCurrentDatabaseItemSpy).toBeCalledTimes(1);
|
||||
expect(setCurrentDatabaseItemSpy).toBeCalledWith(
|
||||
postTestDatabaseItem,
|
||||
true,
|
||||
);
|
||||
@@ -167,8 +167,7 @@ describe("test-adapter", () => {
|
||||
function mockRunTests() {
|
||||
// runTests is an async generator function. This is not directly supported in sinon
|
||||
// However, we can pretend the same thing by just returning an async array.
|
||||
runTestsSpy = sandox.stub();
|
||||
runTestsSpy.returns(
|
||||
runTestsSpy.mockReturnValue(
|
||||
(async function* () {
|
||||
yield Promise.resolve({
|
||||
test: Uri.parse("file:/ab/c/d.ql").fsPath,
|
||||
|
||||
Reference in New Issue
Block a user