Migrate minimal-workspace integration tests to Jest (#1786)

This commit is contained in:
Charis Kyriakou
2022-11-24 16:01:20 +00:00
committed by GitHub
parent c31635fe90
commit 91ca9481eb
11 changed files with 295 additions and 345 deletions

View File

@@ -9,5 +9,6 @@ module.exports = {
"<rootDir>/src/view",
"<rootDir>/test",
"<rootDir>/out/vscode-tests/no-workspace",
"<rootDir>/out/vscode-tests/minimal-workspace",
],
};

View File

@@ -1274,7 +1274,7 @@
"test:view": "jest --projects src/view",
"integration": "npm-run-all integration:*",
"integration:no-workspace": "jest --projects out/vscode-tests/no-workspace",
"integration:minimal-workspace": "node ./out/vscode-tests/run-integration-tests.js minimal-workspace",
"integration:minimal-workspace": "jest --projects out/vscode-tests/minimal-workspace",
"cli-integration": "node ./out/vscode-tests/run-integration-tests.js cli-integration",
"update-vscode": "node ./node_modules/vscode/bin/install",
"format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix",

View File

@@ -1,35 +1,36 @@
import * as assert from "assert";
import * as path from "path";
import * as vscode from "vscode";
import * as determiningSelectedQueryTest from "./determining-selected-query-test";
describe("launching with a minimal workspace", async () => {
// Temporary until Mocha is fully removed. This is necessary for passing timeouts to `it`.
declare let it: jest.It;
describe("launching with a minimal workspace", () => {
const ext = vscode.extensions.getExtension("GitHub.vscode-codeql");
it("should install the extension", () => {
assert(ext);
expect(ext).toBeDefined();
});
// Note, this test will only pass in pristine workspaces. This means that when run locally and you
// reuse an existing workspace that starts with an open ql file, this test will fail. There is
// no need to make any changes since this will still pass on CI.
it("should not activate the extension at first", () => {
assert(ext!.isActive === false);
expect(ext!.isActive).toEqual(false);
});
it("should activate the extension when a .ql file is opened", async function () {
this.timeout(60000);
it("should activate the extension when a .ql file is opened", async () => {
await delay();
const folders = vscode.workspace.workspaceFolders;
assert(folders && folders.length === 1);
expect(folders?.length).toEqual(1);
const folderPath = folders![0].uri.fsPath;
const documentPath = path.resolve(folderPath, "query.ql");
const document = await vscode.workspace.openTextDocument(documentPath);
assert(document.languageId === "ql");
expect(document.languageId).toEqual("ql");
// Delay slightly so that the extension has time to activate.
await delay();
assert(ext!.isActive);
});
expect(ext!.isActive).toBeTruthy();
}, 60_000);
async function delay() {
await new Promise((resolve) => setTimeout(resolve, 4000));

View File

@@ -1,28 +1,15 @@
import * as Sinon from "sinon";
import { expect } from "chai";
import { workspace } from "vscode";
import {
CliConfigListener,
ConfigListener,
QueryHistoryConfigListener,
QueryServerConfigListener,
} from "../../config";
describe("config listeners", function () {
// Because we are adding some extra waiting, need to bump the test timeouts.
this.timeout(5000);
let sandbox: Sinon.SinonSandbox;
beforeEach(() => {
sandbox = Sinon.createSandbox();
});
afterEach(() => {
sandbox.restore();
});
describe("config listeners", () => {
interface TestConfig<T> {
clazz: new () => unknown;
clazz: new () => ConfigListener;
settings: {
name: string;
property: string;
@@ -95,23 +82,14 @@ describe("config listeners", function () {
testConfig.forEach((config) => {
describe(config.clazz.name, () => {
let listener: any;
let spy: Sinon.SinonSpy;
beforeEach(() => {
listener = new config.clazz();
spy = Sinon.spy();
listener.onDidChangeConfiguration(spy);
});
config.settings.forEach((setting) => {
let origValue: any;
let origValue: string | number | boolean | undefined;
beforeEach(async () => {
origValue = workspace.getConfiguration().get(setting.name);
await workspace
.getConfiguration()
.update(setting.name, setting.values[0]);
await wait();
spy.resetHistory();
});
afterEach(async () => {
@@ -120,12 +98,17 @@ describe("config listeners", function () {
});
it(`should listen for changes to '${setting.name}'`, async () => {
const listener = new config.clazz();
const onDidChangeConfiguration = jest.fn();
listener.onDidChangeConfiguration(onDidChangeConfiguration);
await workspace
.getConfiguration()
.update(setting.name, setting.values[1]);
await wait();
expect(listener[setting.property]).to.eq(setting.values[1]);
expect(spy).to.have.been.calledOnce;
const newValue = listener[setting.property as keyof typeof listener];
expect(newValue).toEqual(setting.values[1]);
expect(onDidChangeConfiguration).toHaveBeenCalledTimes(1);
});
});
});

View File

@@ -1,8 +1,6 @@
import * as sinon from "sinon";
import * as tmp from "tmp";
import * as fs from "fs-extra";
import * as path from "path";
import { expect } from "chai";
import { CancellationToken, ExtensionContext, Uri, workspace } from "vscode";
import {
@@ -15,7 +13,7 @@ import {
} from "../../databases";
import { Logger } from "../../logging";
import { ProgressCallback } from "../../commandRunner";
import { CodeQLCliServer } from "../../cli";
import { CodeQLCliServer, DbInfo } from "../../cli";
import {
encodeArchiveBasePath,
encodeSourceArchiveUri,
@@ -31,37 +29,29 @@ describe("databases", () => {
};
let databaseManager: DatabaseManager;
let updateSpy: sinon.SinonSpy;
let getSpy: sinon.SinonStub;
let dbChangedHandler: sinon.SinonSpy;
let registerSpy: sinon.SinonSpy;
let deregisterSpy: sinon.SinonSpy;
let supportsDatabaseRegistrationSpy: sinon.SinonStub;
let supportsLanguageNameSpy: sinon.SinonStub;
let resolveDatabaseSpy: sinon.SinonStub;
let sandbox: sinon.SinonSandbox;
let updateSpy: jest.Mock<Promise<void>, []>;
let registerSpy: jest.Mock<Promise<void>, []>;
let deregisterSpy: jest.Mock<Promise<void>, []>;
let supportsLanguageNameSpy: jest.Mock<Promise<boolean>, []>;
let resolveDatabaseSpy: jest.Mock<Promise<DbInfo>, []>;
let dir: tmp.DirResult;
beforeEach(() => {
dir = tmp.dirSync();
sandbox = sinon.createSandbox();
updateSpy = sandbox.spy();
getSpy = sandbox.stub();
getSpy.returns([]);
registerSpy = sandbox.stub();
deregisterSpy = sandbox.stub();
dbChangedHandler = sandbox.spy();
supportsDatabaseRegistrationSpy = sandbox.stub();
supportsDatabaseRegistrationSpy.resolves(true);
supportsLanguageNameSpy = sandbox.stub();
resolveDatabaseSpy = sandbox.stub();
updateSpy = jest.fn(() => Promise.resolve(undefined));
registerSpy = jest.fn(() => Promise.resolve(undefined));
deregisterSpy = jest.fn(() => Promise.resolve(undefined));
supportsLanguageNameSpy = jest.fn(() => Promise.resolve(true));
resolveDatabaseSpy = jest.fn(() => Promise.resolve({} as DbInfo));
databaseManager = new DatabaseManager(
{
workspaceState: {
update: updateSpy,
get: getSpy,
get: () => [],
},
// pretend like databases added in the temp dir are controlled by the extension
// so that they are deleted upon removal
@@ -77,7 +67,7 @@ describe("databases", () => {
{
cliConstraints: {
supportsLanguageName: supportsLanguageNameSpy,
supportsDatabaseRegistration: supportsDatabaseRegistrationSpy,
supportsDatabaseRegistration: () => true,
},
resolveDatabase: resolveDatabaseSpy,
} as unknown as CodeQLCliServer,
@@ -91,39 +81,38 @@ describe("databases", () => {
// Unfortunately, during a test it is not possible to convert from
// a single root workspace to multi-root, so must stub out relevant
// functions
sandbox.stub(workspace, "updateWorkspaceFolders");
sandbox.spy(workspace, "onDidChangeWorkspaceFolders");
jest.spyOn(workspace, "updateWorkspaceFolders").mockReturnValue(true);
});
afterEach(async () => {
dir.removeCallback();
databaseManager.dispose(testDisposeHandler);
sandbox.restore();
});
it("should fire events when adding and removing a db item", async () => {
const mockDbItem = createMockDB();
const spy = sinon.spy();
databaseManager.onDidChangeDatabaseItem(spy);
const onDidChangeDatabaseItem = jest.fn();
databaseManager.onDidChangeDatabaseItem(onDidChangeDatabaseItem);
await (databaseManager as any).addDatabaseItem(
{} as ProgressCallback,
{} as CancellationToken,
mockDbItem,
);
expect((databaseManager as any)._databaseItems).to.deep.eq([mockDbItem]);
expect(updateSpy).to.have.been.calledWith("databaseList", [
expect((databaseManager as any)._databaseItems).toEqual([mockDbItem]);
expect(updateSpy).toBeCalledWith("databaseList", [
{
options: MOCK_DB_OPTIONS,
uri: dbLocationUri().toString(true),
},
]);
expect(spy).to.have.been.calledWith({
expect(onDidChangeDatabaseItem).toBeCalledWith({
item: undefined,
kind: DatabaseEventKind.Add,
});
sinon.reset();
updateSpy.mockClear();
onDidChangeDatabaseItem.mockClear();
// now remove the item
await databaseManager.removeDatabaseItem(
@@ -131,9 +120,9 @@ describe("databases", () => {
{} as CancellationToken,
mockDbItem,
);
expect((databaseManager as any)._databaseItems).to.deep.eq([]);
expect(updateSpy).to.have.been.calledWith("databaseList", []);
expect(spy).to.have.been.calledWith({
expect((databaseManager as any)._databaseItems).toEqual([]);
expect(updateSpy).toBeCalledWith("databaseList", []);
expect(onDidChangeDatabaseItem).toBeCalledWith({
item: undefined,
kind: DatabaseEventKind.Remove,
});
@@ -141,27 +130,25 @@ describe("databases", () => {
it("should rename a db item and emit an event", async () => {
const mockDbItem = createMockDB();
const spy = sinon.spy();
databaseManager.onDidChangeDatabaseItem(spy);
const onDidChangeDatabaseItem = jest.fn();
databaseManager.onDidChangeDatabaseItem(onDidChangeDatabaseItem);
await (databaseManager as any).addDatabaseItem(
{} as ProgressCallback,
{} as CancellationToken,
mockDbItem,
);
sinon.restore();
await databaseManager.renameDatabaseItem(mockDbItem, "new name");
expect(mockDbItem.name).to.eq("new name");
expect(updateSpy).to.have.been.calledWith("databaseList", [
expect(mockDbItem.name).toBe("new name");
expect(updateSpy).toBeCalledWith("databaseList", [
{
options: { ...MOCK_DB_OPTIONS, displayName: "new name" },
uri: dbLocationUri().toString(true),
},
]);
expect(spy).to.have.been.calledWith({
expect(onDidChangeDatabaseItem).toBeCalledWith({
item: undefined,
kind: DatabaseEventKind.Rename,
});
@@ -169,8 +156,8 @@ describe("databases", () => {
describe("add / remove database items", () => {
it("should add a database item", async () => {
const spy = sandbox.spy();
databaseManager.onDidChangeDatabaseItem(spy);
const onDidChangeDatabaseItem = jest.fn();
databaseManager.onDidChangeDatabaseItem(onDidChangeDatabaseItem);
const mockDbItem = createMockDB();
await (databaseManager as any).addDatabaseItem(
@@ -179,8 +166,8 @@ describe("databases", () => {
mockDbItem,
);
expect(databaseManager.databaseItems).to.deep.eq([mockDbItem]);
expect(updateSpy).to.have.been.calledWith("databaseList", [
expect(databaseManager.databaseItems).toEqual([mockDbItem]);
expect(updateSpy).toBeCalledWith("databaseList", [
{
uri: dbLocationUri().toString(true),
options: MOCK_DB_OPTIONS,
@@ -191,35 +178,36 @@ describe("databases", () => {
item: undefined,
kind: DatabaseEventKind.Add,
};
expect(spy).to.have.been.calledWith(mockEvent);
expect(onDidChangeDatabaseItem).toBeCalledWith(mockEvent);
});
it("should add a database item source archive", async function () {
it("should add a database item source archive", async () => {
const mockDbItem = createMockDB();
mockDbItem.name = "xxx";
await (databaseManager as any).addDatabaseSourceArchiveFolder(mockDbItem);
await databaseManager.addDatabaseSourceArchiveFolder(mockDbItem);
// workspace folders should be updated. We can only check the mocks since
// when running as a test, we are not allowed to update the workspace folders
expect(workspace.updateWorkspaceFolders).to.have.been.calledWith(1, 0, {
expect(workspace.updateWorkspaceFolders).toHaveBeenCalledWith(1, 0, {
name: "[xxx source archive]",
// must use a matcher here since vscode URIs with the same path
// are not always equal due to internal state.
uri: sinon.match.has(
"fsPath",
encodeArchiveBasePath(sourceLocationUri().fsPath).fsPath,
),
uri: expect.objectContaining({
fsPath: encodeArchiveBasePath(sourceLocationUri().fsPath).fsPath,
}),
});
});
it("should remove a database item", async () => {
const mockDbItem = createMockDB();
sandbox.stub(fs, "remove").resolves();
const removeMock = jest
.spyOn(fs, "remove")
.mockImplementation(() => Promise.resolve());
// pretend that this item is the first workspace folder in the list
sandbox
.stub(mockDbItem, "belongsToSourceArchiveExplorerUri")
.returns(true);
jest
.spyOn(mockDbItem, "belongsToSourceArchiveExplorerUri")
.mockReturnValue(true);
await (databaseManager as any).addDatabaseItem(
{} as ProgressCallback,
@@ -227,7 +215,7 @@ describe("databases", () => {
mockDbItem,
);
updateSpy.resetHistory();
updateSpy.mockClear();
await databaseManager.removeDatabaseItem(
{} as ProgressCallback,
@@ -235,30 +223,30 @@ describe("databases", () => {
mockDbItem,
);
expect(databaseManager.databaseItems).to.deep.eq([]);
expect(updateSpy).to.have.been.calledWith("databaseList", []);
expect(databaseManager.databaseItems).toEqual([]);
expect(updateSpy).toBeCalledWith("databaseList", []);
// should remove the folder
expect(workspace.updateWorkspaceFolders).to.have.been.calledWith(0, 1);
expect(workspace.updateWorkspaceFolders).toBeCalledWith(0, 1);
// should also delete the db contents
expect(fs.remove).to.have.been.calledWith(mockDbItem.databaseUri.fsPath);
expect(removeMock).toBeCalledWith(mockDbItem.databaseUri.fsPath);
});
it("should remove a database item outside of the extension controlled area", async () => {
const mockDbItem = createMockDB();
sandbox.stub(fs, "remove").resolves();
const removeMock = jest.spyOn(fs, "remove");
removeMock.mockReset().mockImplementation(() => Promise.resolve());
// pretend that this item is the first workspace folder in the list
sandbox
.stub(mockDbItem, "belongsToSourceArchiveExplorerUri")
.returns(true);
jest
.spyOn(mockDbItem, "belongsToSourceArchiveExplorerUri")
.mockReturnValue(true);
await (databaseManager as any).addDatabaseItem(
{} as ProgressCallback,
{} as CancellationToken,
mockDbItem,
);
updateSpy.resetHistory();
updateSpy.mockClear();
// pretend that the database location is not controlled by the extension
(databaseManager as any).ctx.storagePath = "hucairz";
@@ -269,13 +257,13 @@ describe("databases", () => {
mockDbItem,
);
expect(databaseManager.databaseItems).to.deep.eq([]);
expect(updateSpy).to.have.been.calledWith("databaseList", []);
expect(databaseManager.databaseItems).toEqual([]);
expect(updateSpy).toBeCalledWith("databaseList", []);
// should remove the folder
expect(workspace.updateWorkspaceFolders).to.have.been.calledWith(0, 1);
expect(workspace.updateWorkspaceFolders).toBeCalledWith(0, 1);
// should NOT delete the db contents
expect(fs.remove).not.to.have.been.called;
expect(removeMock).not.toBeCalled();
});
it("should register and deregister a database when adding and removing it", async () => {
@@ -283,7 +271,7 @@ describe("databases", () => {
// registration messages.
const mockDbItem = createMockDB();
sandbox.stub(fs, "remove").resolves();
jest.spyOn(fs, "remove").mockImplementation(() => Promise.resolve());
await (databaseManager as any).addDatabaseItem(
{} as ProgressCallback,
@@ -291,7 +279,7 @@ describe("databases", () => {
mockDbItem,
);
// Should have registered this database
expect(registerSpy).to.have.been.calledWith({}, {}, mockDbItem);
expect(registerSpy).toBeCalledWith({}, {}, mockDbItem);
await databaseManager.removeDatabaseItem(
{} as ProgressCallback,
@@ -300,7 +288,7 @@ describe("databases", () => {
);
// Should have deregistered this database
expect(deregisterSpy).to.have.been.calledWith({}, {}, mockDbItem);
expect(deregisterSpy).toBeCalledWith({}, {}, mockDbItem);
});
});
@@ -308,13 +296,15 @@ describe("databases", () => {
it("should fail to resolve when not a uri", () => {
const db = createMockDB(Uri.parse("file:/sourceArchive-uri/"));
(db as any)._contents.sourceArchiveUri = undefined;
expect(() => db.resolveSourceFile("abc")).to.throw("Scheme is missing");
expect(() => db.resolveSourceFile("abc")).toThrowError(
"Scheme is missing",
);
});
it("should fail to resolve when not a file uri", () => {
const db = createMockDB(Uri.parse("file:/sourceArchive-uri/"));
(db as any)._contents.sourceArchiveUri = undefined;
expect(() => db.resolveSourceFile("http://abc")).to.throw(
expect(() => db.resolveSourceFile("http://abc")).toThrowError(
"Invalid uri scheme",
);
});
@@ -324,14 +314,14 @@ describe("databases", () => {
const db = createMockDB(Uri.parse("file:/sourceArchive-uri/"));
(db as any)._contents.sourceArchiveUri = undefined;
const resolved = db.resolveSourceFile(undefined);
expect(resolved.toString(true)).to.eq(dbLocationUri().toString(true));
expect(resolved.toString(true)).toBe(dbLocationUri().toString(true));
});
it("should resolve an empty file", () => {
const db = createMockDB(Uri.parse("file:/sourceArchive-uri/"));
(db as any)._contents.sourceArchiveUri = undefined;
const resolved = db.resolveSourceFile("file:");
expect(resolved.toString()).to.eq("file:///");
expect(resolved.toString()).toBe("file:///");
});
});
@@ -347,7 +337,7 @@ describe("databases", () => {
// must recreate an encoded archive uri instead of typing out the
// text since the uris will be different on windows and ubuntu.
expect(resolved.toString()).to.eq(
expect(resolved.toString()).toBe(
encodeSourceArchiveUri({
sourceArchiveZipPath: "sourceArchive-uri",
pathWithinSourceArchive: "def/abc",
@@ -366,7 +356,7 @@ describe("databases", () => {
// must recreate an encoded archive uri instead of typing out the
// text since the uris will be different on windows and ubuntu.
expect(resolved.toString()).to.eq(
expect(resolved.toString()).toBe(
encodeSourceArchiveUri({
sourceArchiveZipPath: "sourceArchive-uri",
pathWithinSourceArchive: "def/abc",
@@ -382,7 +372,7 @@ describe("databases", () => {
}),
);
const resolved = db.resolveSourceFile("file:");
expect(resolved.toString()).to.eq(
expect(resolved.toString()).toBe(
"codeql-zip-archive://1-18/sourceArchive-uri/def/",
);
});
@@ -391,145 +381,145 @@ describe("databases", () => {
it("should handle an empty file", () => {
const db = createMockDB(Uri.parse("file:/sourceArchive-uri/"));
const resolved = db.resolveSourceFile("");
expect(resolved.toString()).to.eq("file:///sourceArchive-uri/");
expect(resolved.toString()).toBe("file:///sourceArchive-uri/");
});
});
it("should not support the primary language", async () => {
supportsLanguageNameSpy.resolves(false);
supportsLanguageNameSpy.mockResolvedValue(false);
const result = await (databaseManager as any).getPrimaryLanguage("hucairz");
expect(result).to.be.undefined;
expect(result).toBeUndefined();
});
it("should get the primary language", async () => {
supportsLanguageNameSpy.resolves(true);
resolveDatabaseSpy.resolves({
supportsLanguageNameSpy.mockResolvedValue(true);
resolveDatabaseSpy.mockResolvedValue({
languages: ["python"],
});
} as unknown as DbInfo);
const result = await (databaseManager as any).getPrimaryLanguage("hucairz");
expect(result).to.eq("python");
expect(result).toBe("python");
});
it("should handle missing the primary language", async () => {
supportsLanguageNameSpy.resolves(true);
resolveDatabaseSpy.resolves({
supportsLanguageNameSpy.mockResolvedValue(true);
resolveDatabaseSpy.mockResolvedValue({
languages: [],
});
} as unknown as DbInfo);
const result = await (databaseManager as any).getPrimaryLanguage("hucairz");
expect(result).to.eq("");
expect(result).toBe("");
});
describe("isAffectedByTest", () => {
const directoryStats = new fs.Stats();
const fileStats = new fs.Stats();
before(() => {
sinon.stub(directoryStats, "isDirectory").returns(true);
sinon.stub(fileStats, "isDirectory").returns(false);
beforeEach(() => {
jest.spyOn(directoryStats, "isDirectory").mockReturnValue(true);
jest.spyOn(fileStats, "isDirectory").mockReturnValue(false);
});
it("should return true for testproj database in test directory", async () => {
sandbox.stub(fs, "stat").resolves(directoryStats);
jest.spyOn(fs, "stat").mockResolvedValue(directoryStats);
const db = createMockDB(
sourceLocationUri(),
Uri.file("/path/to/dir/dir.testproj"),
);
expect(await db.isAffectedByTest("/path/to/dir")).to.true;
expect(await db.isAffectedByTest("/path/to/dir")).toBe(true);
});
it("should return false for non-existent test directory", async () => {
sandbox.stub(fs, "stat").throws("Simulated Error: ENOENT");
jest.spyOn(fs, "stat").mockImplementation(() => {
throw new Error("Simulated Error: ENOENT");
});
const db = createMockDB(
sourceLocationUri(),
Uri.file("/path/to/dir/dir.testproj"),
);
expect(await db.isAffectedByTest("/path/to/dir")).to.false;
expect(await db.isAffectedByTest("/path/to/dir")).toBe(false);
});
it("should return false for non-testproj database in test directory", async () => {
sandbox.stub(fs, "stat").resolves(directoryStats);
jest.spyOn(fs, "stat").mockResolvedValue(directoryStats);
const db = createMockDB(
sourceLocationUri(),
Uri.file("/path/to/dir/dir.proj"),
);
expect(await db.isAffectedByTest("/path/to/dir")).to.false;
expect(await db.isAffectedByTest("/path/to/dir")).toBe(false);
});
it("should return false for testproj database outside test directory", async () => {
sandbox.stub(fs, "stat").resolves(directoryStats);
jest.spyOn(fs, "stat").mockResolvedValue(directoryStats);
const db = createMockDB(
sourceLocationUri(),
Uri.file("/path/to/other/dir.testproj"),
);
expect(await db.isAffectedByTest("/path/to/dir")).to.false;
expect(await db.isAffectedByTest("/path/to/dir")).toBe(false);
});
it("should return false for testproj database for prefix directory", async () => {
sandbox.stub(fs, "stat").resolves(directoryStats);
jest.spyOn(fs, "stat").mockResolvedValue(directoryStats);
const db = createMockDB(
sourceLocationUri(),
Uri.file("/path/to/dir/dir.testproj"),
);
// /path/to/d is a prefix of /path/to/dir/dir.testproj, but
// /path/to/dir/dir.testproj is not under /path/to/d
expect(await db.isAffectedByTest("/path/to/d")).to.false;
expect(await db.isAffectedByTest("/path/to/d")).toBe(false);
});
it("should return true for testproj database for test file", async () => {
sandbox.stub(fs, "stat").resolves(fileStats);
jest.spyOn(fs, "stat").mockResolvedValue(fileStats);
const db = createMockDB(
sourceLocationUri(),
Uri.file("/path/to/dir/dir.testproj"),
);
expect(await db.isAffectedByTest("/path/to/dir/test.ql")).to.true;
expect(await db.isAffectedByTest("/path/to/dir/test.ql")).toBe(true);
});
it("should return false for non-existent test file", async () => {
sandbox.stub(fs, "stat").throws("Simulated Error: ENOENT");
jest.spyOn(fs, "stat").mockImplementation(() => {
throw new Error("Simulated Error: ENOENT");
});
const db = createMockDB(
sourceLocationUri(),
Uri.file("/path/to/dir/dir.testproj"),
);
expect(await db.isAffectedByTest("/path/to/dir/test.ql")).to.false;
expect(await db.isAffectedByTest("/path/to/dir/test.ql")).toBe(false);
});
it("should return false for non-testproj database for test file", async () => {
sandbox.stub(fs, "stat").resolves(fileStats);
jest.spyOn(fs, "stat").mockResolvedValue(fileStats);
const db = createMockDB(
sourceLocationUri(),
Uri.file("/path/to/dir/dir.proj"),
);
expect(await db.isAffectedByTest("/path/to/dir/test.ql")).to.false;
expect(await db.isAffectedByTest("/path/to/dir/test.ql")).toBe(false);
});
it("should return false for testproj database not matching test file", async () => {
sandbox.stub(fs, "stat").resolves(fileStats);
jest.spyOn(fs, "stat").mockResolvedValue(fileStats);
const db = createMockDB(
sourceLocationUri(),
Uri.file("/path/to/dir/dir.testproj"),
);
expect(await db.isAffectedByTest("/path/to/test.ql")).to.false;
expect(await db.isAffectedByTest("/path/to/test.ql")).toBe(false);
});
});
describe("findSourceArchive", function () {
// not sure why, but some of these tests take more than two seconds to run.
this.timeout(5000);
describe("findSourceArchive", () => {
["src", "output/src_archive"].forEach((name) => {
it(`should find source folder in ${name}`, async () => {
const uri = Uri.file(path.join(dir.name, name));
fs.createFileSync(path.join(uri.fsPath, "hucairz.txt"));
const srcUri = await findSourceArchive(dir.name);
expect(srcUri!.fsPath).to.eq(uri.fsPath);
expect(srcUri!.fsPath).toBe(uri.fsPath);
});
it(`should find source archive in ${name}.zip`, async () => {
const uri = Uri.file(path.join(dir.name, name + ".zip"));
fs.createFileSync(uri.fsPath);
const srcUri = await findSourceArchive(dir.name);
expect(srcUri!.fsPath).to.eq(uri.fsPath);
expect(srcUri!.fsPath).toBe(uri.fsPath);
});
it(`should prioritize ${name}.zip over ${name}`, async () => {
@@ -540,7 +530,7 @@ describe("databases", () => {
fs.createFileSync(path.join(uriFolder.fsPath, "hucairz.txt"));
const srcUri = await findSourceArchive(dir.name);
expect(srcUri!.fsPath).to.eq(uri.fsPath);
expect(srcUri!.fsPath).toBe(uri.fsPath);
});
});
@@ -551,7 +541,7 @@ describe("databases", () => {
fs.createFileSync(uriSrcArchive.fsPath);
const resultUri = await findSourceArchive(dir.name);
expect(resultUri!.fsPath).to.eq(uriSrc.fsPath);
expect(resultUri!.fsPath).toBe(uriSrc.fsPath);
});
});
@@ -568,7 +558,7 @@ describe("databases", () => {
datasetUri: databaseUri,
} as DatabaseContents,
MOCK_DB_OPTIONS,
dbChangedHandler,
() => void 0,
);
}

View File

@@ -1,21 +1,16 @@
import * as vscode from "vscode";
import { expect } from "chai";
import * as path from "path";
import * as fs from "fs-extra";
import * as pq from "proxyquire";
import { DbConfig } from "../../../databases/config/db-config";
import { DbManager } from "../../../databases/db-manager";
import { DbConfigStore } from "../../../databases/config/db-config-store";
import { DbTreeDataProvider } from "../../../databases/ui/db-tree-data-provider";
import { DbPanel } from "../../../databases/ui/db-panel";
import { DbItemKind, LocalDatabaseDbItem } from "../../../databases/db-item";
import { DbTreeViewItem } from "../../../databases/ui/db-tree-view-item";
import { ExtensionApp } from "../../../common/vscode/vscode-app";
import { createMockExtensionContext } from "../../factories/extension-context";
const proxyquire = pq.noPreserveCache();
describe("db panel", async () => {
describe("db panel", () => {
const workspaceStoragePath = path.join(__dirname, "test-workspace-storage");
const globalStoragePath = path.join(__dirname, "test-global-storage");
const extensionPath = path.join(__dirname, "../../../../");
@@ -26,9 +21,8 @@ describe("db panel", async () => {
let dbTreeDataProvider: DbTreeDataProvider;
let dbManager: DbManager;
let dbConfigStore: DbConfigStore;
let dbPanel: DbPanel;
before(async () => {
beforeAll(async () => {
const extensionContext = createMockExtensionContext({
extensionPath,
globalStoragePath,
@@ -40,22 +34,6 @@ describe("db panel", async () => {
dbConfigStore = new DbConfigStore(app);
dbManager = new DbManager(app, dbConfigStore);
// Create a modified version of the DbPanel module that allows
// us to override the creation of the DbTreeDataProvider
const mod = proxyquire("../../../databases/ui/db-panel", {
"./db-tree-data-provider": {
DbTreeDataProvider: class {
constructor() {
return dbTreeDataProvider;
}
},
},
});
// Initialize the panel using the modified module
dbPanel = new mod.DbPanel(dbManager) as DbPanel;
await dbPanel.initialize();
});
beforeEach(async () => {
@@ -85,39 +63,39 @@ describe("db panel", async () => {
const dbTreeItems = await dbTreeDataProvider.getChildren();
expect(dbTreeItems).to.be.ok;
expect(dbTreeItems).toBeTruthy();
const items = dbTreeItems!;
expect(items.length).to.equal(2);
expect(items.length).toBe(2);
const remoteRootNode = items[0];
expect(remoteRootNode.dbItem).to.be.ok;
expect(remoteRootNode.dbItem?.kind).to.equal(DbItemKind.RootRemote);
expect(remoteRootNode.label).to.equal("remote");
expect(remoteRootNode.tooltip).to.equal("Remote databases");
expect(remoteRootNode.collapsibleState).to.equal(
expect(remoteRootNode.dbItem).toBeTruthy();
expect(remoteRootNode.dbItem?.kind).toBe(DbItemKind.RootRemote);
expect(remoteRootNode.label).toBe("remote");
expect(remoteRootNode.tooltip).toBe("Remote databases");
expect(remoteRootNode.collapsibleState).toBe(
vscode.TreeItemCollapsibleState.Collapsed,
);
expect(remoteRootNode.children).to.be.ok;
expect(remoteRootNode.children.length).to.equal(3);
expect(remoteRootNode.children).toBeTruthy();
expect(remoteRootNode.children.length).toBe(3);
const systemDefinedListItems = remoteRootNode.children.filter(
(item) => item.dbItem?.kind === DbItemKind.RemoteSystemDefinedList,
);
expect(systemDefinedListItems.length).to.equal(3);
expect(systemDefinedListItems.length).toBe(3);
checkRemoteSystemDefinedListItem(systemDefinedListItems[0], 10);
checkRemoteSystemDefinedListItem(systemDefinedListItems[1], 100);
checkRemoteSystemDefinedListItem(systemDefinedListItems[2], 1000);
const localRootNode = items[1];
expect(localRootNode.dbItem).to.be.ok;
expect(localRootNode.dbItem?.kind).to.equal(DbItemKind.RootLocal);
expect(localRootNode.label).to.equal("local");
expect(localRootNode.tooltip).to.equal("Local databases");
expect(localRootNode.collapsibleState).to.equal(
expect(localRootNode.dbItem).toBeTruthy();
expect(localRootNode.dbItem?.kind).toBe(DbItemKind.RootLocal);
expect(localRootNode.label).toBe("local");
expect(localRootNode.tooltip).toBe("Local databases");
expect(localRootNode.collapsibleState).toBe(
vscode.TreeItemCollapsibleState.Collapsed,
);
expect(localRootNode.children).to.be.ok;
expect(localRootNode.children.length).to.equal(0);
expect(localRootNode.children).toBeTruthy();
expect(localRootNode.children.length).toBe(0);
});
it("should render remote repository list nodes", async () => {
@@ -148,27 +126,27 @@ describe("db panel", async () => {
const dbTreeItems = await dbTreeDataProvider.getChildren();
expect(dbTreeItems).to.be.ok;
expect(dbTreeItems).toBeTruthy();
const items = dbTreeItems!;
expect(items.length).to.equal(2);
expect(items.length).toBe(2);
const remoteRootNode = items[0];
expect(remoteRootNode.dbItem).to.be.ok;
expect(remoteRootNode.collapsibleState).to.equal(
expect(remoteRootNode.dbItem).toBeTruthy();
expect(remoteRootNode.collapsibleState).toBe(
vscode.TreeItemCollapsibleState.Collapsed,
);
expect(remoteRootNode.children).to.be.ok;
expect(remoteRootNode.children.length).to.equal(5);
expect(remoteRootNode.children).toBeTruthy();
expect(remoteRootNode.children.length).toBe(5);
const systemDefinedListItems = remoteRootNode.children.filter(
(item) => item.dbItem?.kind === DbItemKind.RemoteSystemDefinedList,
);
expect(systemDefinedListItems.length).to.equal(3);
expect(systemDefinedListItems.length).toBe(3);
const userDefinedListItems = remoteRootNode.children.filter(
(item) => item.dbItem?.kind === DbItemKind.RemoteUserDefinedList,
);
expect(userDefinedListItems.length).to.equal(2);
expect(userDefinedListItems.length).toBe(2);
checkUserDefinedListItem(userDefinedListItems[0], "my-list-1", [
"owner1/repo1",
"owner1/repo2",
@@ -199,22 +177,22 @@ describe("db panel", async () => {
const dbTreeItems = await dbTreeDataProvider.getChildren();
expect(dbTreeItems).to.be.ok;
expect(dbTreeItems).toBeTruthy();
const items = dbTreeItems!;
expect(items.length).to.equal(2);
expect(items.length).toBe(2);
const remoteRootNode = items[0];
expect(remoteRootNode.dbItem).to.be.ok;
expect(remoteRootNode.collapsibleState).to.equal(
expect(remoteRootNode.dbItem).toBeTruthy();
expect(remoteRootNode.collapsibleState).toBe(
vscode.TreeItemCollapsibleState.Collapsed,
);
expect(remoteRootNode.children).to.be.ok;
expect(remoteRootNode.children.length).to.equal(5);
expect(remoteRootNode.children).toBeTruthy();
expect(remoteRootNode.children.length).toBe(5);
const ownerListItems = remoteRootNode.children.filter(
(item) => item.dbItem?.kind === DbItemKind.RemoteOwner,
);
expect(ownerListItems.length).to.equal(2);
expect(ownerListItems.length).toBe(2);
checkOwnerItem(ownerListItems[0], "owner1");
checkOwnerItem(ownerListItems[1], "owner2");
});
@@ -238,22 +216,22 @@ describe("db panel", async () => {
const dbTreeItems = await dbTreeDataProvider.getChildren();
expect(dbTreeItems).to.be.ok;
expect(dbTreeItems).toBeTruthy();
const items = dbTreeItems!;
expect(items.length).to.equal(2);
expect(items.length).toBe(2);
const remoteRootNode = items[0];
expect(remoteRootNode.dbItem).to.be.ok;
expect(remoteRootNode.collapsibleState).to.equal(
expect(remoteRootNode.dbItem).toBeTruthy();
expect(remoteRootNode.collapsibleState).toBe(
vscode.TreeItemCollapsibleState.Collapsed,
);
expect(remoteRootNode.children).to.be.ok;
expect(remoteRootNode.children.length).to.equal(5);
expect(remoteRootNode.children).toBeTruthy();
expect(remoteRootNode.children.length).toBe(5);
const repoItems = remoteRootNode.children.filter(
(item) => item.dbItem?.kind === DbItemKind.RemoteRepo,
);
expect(repoItems.length).to.equal(2);
expect(repoItems.length).toBe(2);
checkRemoteRepoItem(repoItems[0], "owner1/repo1");
checkRemoteRepoItem(repoItems[1], "owner1/repo2");
});
@@ -306,22 +284,22 @@ describe("db panel", async () => {
const dbTreeItems = await dbTreeDataProvider.getChildren();
expect(dbTreeItems).to.be.ok;
expect(dbTreeItems).toBeTruthy();
const items = dbTreeItems!;
expect(items.length).to.equal(2);
expect(items.length).toBe(2);
const localRootNode = items[1];
expect(localRootNode.dbItem).to.be.ok;
expect(localRootNode.collapsibleState).to.equal(
expect(localRootNode.dbItem).toBeTruthy();
expect(localRootNode.collapsibleState).toBe(
vscode.TreeItemCollapsibleState.Collapsed,
);
expect(localRootNode.children).to.be.ok;
expect(localRootNode.children.length).to.equal(2);
expect(localRootNode.children).toBeTruthy();
expect(localRootNode.children.length).toBe(2);
const localListItems = localRootNode.children.filter(
(item) => item.dbItem?.kind === DbItemKind.LocalList,
);
expect(localListItems.length).to.equal(2);
expect(localListItems.length).toBe(2);
checkLocalListItem(localListItems[0], "my-list-1", [
{
kind: DbItemKind.LocalDatabase,
@@ -381,22 +359,22 @@ describe("db panel", async () => {
const dbTreeItems = await dbTreeDataProvider.getChildren();
expect(dbTreeItems).to.be.ok;
expect(dbTreeItems).toBeTruthy();
const items = dbTreeItems!;
expect(items.length).to.equal(2);
expect(items.length).toBe(2);
const localRootNode = items[1];
expect(localRootNode.dbItem).to.be.ok;
expect(localRootNode.collapsibleState).to.equal(
expect(localRootNode.dbItem).toBeTruthy();
expect(localRootNode.collapsibleState).toBe(
vscode.TreeItemCollapsibleState.Collapsed,
);
expect(localRootNode.children).to.be.ok;
expect(localRootNode.children.length).to.equal(2);
expect(localRootNode.children).toBeTruthy();
expect(localRootNode.children.length).toBe(2);
const localDatabaseItems = localRootNode.children.filter(
(item) => item.dbItem?.kind === DbItemKind.LocalDatabase,
);
expect(localDatabaseItems.length).to.equal(2);
expect(localDatabaseItems.length).toBe(2);
checkLocalDatabaseItem(localDatabaseItems[0], {
kind: DbItemKind.LocalDatabase,
databaseName: "db1",
@@ -428,12 +406,10 @@ describe("db panel", async () => {
item: DbTreeViewItem,
n: number,
): void {
expect(item.label).to.equal(`Top ${n} repositories`);
expect(item.tooltip).to.equal(`Top ${n} repositories of a language`);
expect(item.iconPath).to.deep.equal(new vscode.ThemeIcon("github"));
expect(item.collapsibleState).to.equal(
vscode.TreeItemCollapsibleState.None,
);
expect(item.label).toBe(`Top ${n} repositories`);
expect(item.tooltip).toBe(`Top ${n} repositories of a language`);
expect(item.iconPath).toEqual(new vscode.ThemeIcon("github"));
expect(item.collapsibleState).toBe(vscode.TreeItemCollapsibleState.None);
}
function checkUserDefinedListItem(
@@ -441,14 +417,14 @@ describe("db panel", async () => {
listName: string,
repos: string[],
): void {
expect(item.label).to.equal(listName);
expect(item.tooltip).to.be.undefined;
expect(item.iconPath).to.be.undefined;
expect(item.collapsibleState).to.equal(
expect(item.label).toBe(listName);
expect(item.tooltip).toBeUndefined();
expect(item.iconPath).toBeUndefined();
expect(item.collapsibleState).toBe(
vscode.TreeItemCollapsibleState.Collapsed,
);
expect(item.children).to.be.ok;
expect(item.children.length).to.equal(repos.length);
expect(item.children).toBeTruthy();
expect(item.children.length).toBe(repos.length);
for (let i = 0; i < repos.length; i++) {
checkRemoteRepoItem(item.children[i], repos[i]);
@@ -456,23 +432,19 @@ describe("db panel", async () => {
}
function checkOwnerItem(item: DbTreeViewItem, ownerName: string): void {
expect(item.label).to.equal(ownerName);
expect(item.tooltip).to.be.undefined;
expect(item.iconPath).to.deep.equal(new vscode.ThemeIcon("organization"));
expect(item.collapsibleState).to.equal(
vscode.TreeItemCollapsibleState.None,
);
expect(item.children).to.be.ok;
expect(item.children.length).to.equal(0);
expect(item.label).toBe(ownerName);
expect(item.tooltip).toBeUndefined();
expect(item.iconPath).toEqual(new vscode.ThemeIcon("organization"));
expect(item.collapsibleState).toBe(vscode.TreeItemCollapsibleState.None);
expect(item.children).toBeTruthy();
expect(item.children.length).toBe(0);
}
function checkRemoteRepoItem(item: DbTreeViewItem, repoName: string): void {
expect(item.label).to.equal(repoName);
expect(item.tooltip).to.be.undefined;
expect(item.iconPath).to.deep.equal(new vscode.ThemeIcon("database"));
expect(item.collapsibleState).to.equal(
vscode.TreeItemCollapsibleState.None,
);
expect(item.label).toBe(repoName);
expect(item.tooltip).toBeUndefined();
expect(item.iconPath).toEqual(new vscode.ThemeIcon("database"));
expect(item.collapsibleState).toBe(vscode.TreeItemCollapsibleState.None);
}
function checkLocalListItem(
@@ -480,14 +452,14 @@ describe("db panel", async () => {
listName: string,
databases: LocalDatabaseDbItem[],
): void {
expect(item.label).to.equal(listName);
expect(item.tooltip).to.be.undefined;
expect(item.iconPath).to.be.undefined;
expect(item.collapsibleState).to.equal(
expect(item.label).toBe(listName);
expect(item.tooltip).toBeUndefined();
expect(item.iconPath).toBeUndefined();
expect(item.collapsibleState).toBe(
vscode.TreeItemCollapsibleState.Collapsed,
);
expect(item.children).to.be.ok;
expect(item.children.length).to.equal(databases.length);
expect(item.children).toBeTruthy();
expect(item.children.length).toBe(databases.length);
for (let i = 0; i < databases.length; i++) {
checkLocalDatabaseItem(item.children[i], databases[i]);
@@ -498,11 +470,9 @@ describe("db panel", async () => {
item: DbTreeViewItem,
database: LocalDatabaseDbItem,
): void {
expect(item.label).to.equal(database.databaseName);
expect(item.tooltip).to.equal(`Language: ${database.language}`);
expect(item.iconPath).to.deep.equal(new vscode.ThemeIcon("database"));
expect(item.collapsibleState).to.equal(
vscode.TreeItemCollapsibleState.None,
);
expect(item.label).toBe(database.databaseName);
expect(item.tooltip).toBe(`Language: ${database.language}`);
expect(item.iconPath).toEqual(new vscode.ThemeIcon("database"));
expect(item.collapsibleState).toBe(vscode.TreeItemCollapsibleState.None);
}
});

View File

@@ -1,4 +1,3 @@
import { expect } from "chai";
import * as path from "path";
import * as vscode from "vscode";
import { Uri } from "vscode";
@@ -13,51 +12,49 @@ async function showQlDocument(name: string): Promise<vscode.TextDocument> {
}
export function run() {
describe("Determining selected query", async () => {
describe("Determining selected query", () => {
it("should allow ql files to be queried", async () => {
const q = await determineSelectedQuery(
Uri.parse("file:///tmp/queryname.ql"),
false,
);
expect(q.queryPath).to.equal(path.join("/", "tmp", "queryname.ql"));
expect(q.quickEvalPosition).to.equal(undefined);
expect(q.queryPath).toBe(path.join("/", "tmp", "queryname.ql"));
expect(q.quickEvalPosition).toBeUndefined();
});
it("should allow ql files to be quick-evaled", async () => {
const doc = await showQlDocument("query.ql");
const q = await determineSelectedQuery(doc.uri, true);
expect(q.queryPath).to.satisfy((p: string) =>
p.endsWith(path.join("ql-vscode", "test", "data", "query.ql")),
);
expect(
q.queryPath.endsWith(
path.join("ql-vscode", "test", "data", "query.ql"),
),
).toBe(true);
});
it("should allow qll files to be quick-evaled", async () => {
const doc = await showQlDocument("library.qll");
const q = await determineSelectedQuery(doc.uri, true);
expect(q.queryPath).to.satisfy((p: string) =>
p.endsWith(path.join("ql-vscode", "test", "data", "library.qll")),
);
expect(
q.queryPath.endsWith(
path.join("ql-vscode", "test", "data", "library.qll"),
),
).toBe(true);
});
it("should reject non-ql files when running a query", async () => {
await expect(
determineSelectedQuery(Uri.parse("file:///tmp/queryname.txt"), false),
).to.be.rejectedWith(
Error,
"The selected resource is not a CodeQL query file",
);
).rejects.toThrow("The selected resource is not a CodeQL query file");
await expect(
determineSelectedQuery(Uri.parse("file:///tmp/queryname.qll"), false),
).to.be.rejectedWith(
Error,
"The selected resource is not a CodeQL query file",
);
).rejects.toThrow("The selected resource is not a CodeQL query file");
});
it("should reject non-ql[l] files when running a quick eval", async () => {
await expect(
determineSelectedQuery(Uri.parse("file:///tmp/queryname.txt"), true),
).to.be.rejectedWith(Error, "The selected resource is not a CodeQL file");
).rejects.toThrow("The selected resource is not a CodeQL file");
});
});
}

View File

@@ -1,14 +0,0 @@
import "source-map-support/register";
import * as sinonChai from "sinon-chai";
import * as chai from "chai";
import "chai/register-should";
import * as chaiAsPromised from "chai-as-promised";
import { runTestsInDirectory } from "../index-template";
chai.use(chaiAsPromised);
chai.use(sinonChai);
export function run(): Promise<void> {
return runTestsInDirectory(__dirname);
}

View File

@@ -0,0 +1,17 @@
import * as path from "path";
import { RunnerOptions } from "jest-runner-vscode";
import baseConfig, { rootDir } from "../jest-runner-vscode.config.base";
const config: RunnerOptions = {
...baseConfig,
launchArgs: [
...(baseConfig.launchArgs ?? []),
"--disable-extensions",
path.resolve(rootDir, "test/data"),
],
};
// We are purposefully not using export default here since that would result in an ESModule, which doesn't seem to be
// supported properly by jest-runner-vscode (cosmiconfig doesn't really seem to support it).
module.exports = config;

View File

@@ -0,0 +1,9 @@
import type { Config } from "jest";
import baseConfig from "../jest.config.base";
const config: Config = {
...baseConfig,
};
export default config;

View File

@@ -1,14 +1,10 @@
import { Uri, WorkspaceFolder } from "vscode";
import { expect } from "chai";
import * as fs from "fs-extra";
import * as sinon from "sinon";
import { QLTestDiscovery } from "../../qltest-discovery";
describe("qltest-discovery", () => {
describe("discoverTests", () => {
let sandbox: sinon.SinonSandbox;
const baseUri = Uri.parse("file:/a/b");
const baseDir = baseUri.fsPath;
const cDir = Uri.parse("file:/a/b/c").fsPath;
@@ -19,7 +15,6 @@ describe("qltest-discovery", () => {
let qlTestDiscover: QLTestDiscovery;
beforeEach(() => {
sandbox = sinon.createSandbox();
qlTestDiscover = new QLTestDiscovery(
{
uri: baseUri,
@@ -37,47 +32,48 @@ describe("qltest-discovery", () => {
);
});
afterEach(() => {
sandbox.restore();
});
it("should run discovery", async () => {
sandbox.stub(fs, "pathExists").resolves(true);
jest
.spyOn(fs, "pathExists")
.mockImplementation(() => Promise.resolve(true));
const result = await (qlTestDiscover as any).discover();
expect(result.watchPath).to.eq(baseDir);
expect(result.testDirectory.path).to.eq(baseDir);
expect(result.testDirectory.name).to.eq("My tests");
expect(result.watchPath).toBe(baseDir);
expect(result.testDirectory.path).toBe(baseDir);
expect(result.testDirectory.name).toBe("My tests");
let children = result.testDirectory.children;
expect(children[0].path).to.eq(cDir);
expect(children[0].name).to.eq("c");
expect(children.length).to.eq(1);
expect(children[0].path).toBe(cDir);
expect(children[0].name).toBe("c");
expect(children.length).toBe(1);
children = children[0].children;
expect(children[0].path).to.eq(dFile);
expect(children[0].name).to.eq("d.ql");
expect(children[1].path).to.eq(eFile);
expect(children[1].name).to.eq("e.ql");
expect(children[0].path).toBe(dFile);
expect(children[0].name).toBe("d.ql");
expect(children[1].path).toBe(eFile);
expect(children[1].name).toBe("e.ql");
// A merged foler
expect(children[2].path).to.eq(hDir);
expect(children[2].name).to.eq("f / g / h");
expect(children.length).to.eq(3);
expect(children[2].path).toBe(hDir);
expect(children[2].name).toBe("f / g / h");
expect(children.length).toBe(3);
children = children[2].children;
expect(children[0].path).to.eq(iFile);
expect(children[0].name).to.eq("i.ql");
expect(children[0].path).toBe(iFile);
expect(children[0].name).toBe("i.ql");
});
it("should avoid discovery if a folder does not exist", async () => {
sandbox.stub(fs, "pathExists").resolves(false);
const result = await (qlTestDiscover as any).discover();
expect(result.watchPath).to.eq(baseDir);
expect(result.testDirectory.path).to.eq(baseDir);
expect(result.testDirectory.name).to.eq("My tests");
jest
.spyOn(fs, "pathExists")
.mockImplementation(() => Promise.resolve(false));
expect(result.testDirectory.children.length).to.eq(0);
const result = await (qlTestDiscover as any).discover();
expect(result.watchPath).toBe(baseDir);
expect(result.testDirectory.path).toBe(baseDir);
expect(result.testDirectory.name).toBe("My tests");
expect(result.testDirectory.children.length).toBe(0);
});
});
});