From c9e87eff56ea2d78439a52ea48aab105da59df5c Mon Sep 17 00:00:00 2001 From: Alexander Eyers-Taylor Date: Tue, 25 Oct 2022 19:19:57 +0000 Subject: [PATCH 01/58] Update languageserver-client --- extensions/ql-vscode/package-lock.json | 88 ++++++++++--------- extensions/ql-vscode/package.json | 4 +- extensions/ql-vscode/src/extension.ts | 8 +- extensions/ql-vscode/src/ide-server.ts | 2 +- .../src/legacy-query-server/run-queries.ts | 4 +- extensions/ql-vscode/src/quick-query.ts | 4 +- 6 files changed, 57 insertions(+), 53 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 3da9ca091..22d294dda 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -43,7 +43,7 @@ "unzipper": "~0.10.5", "vscode-extension-telemetry": "^0.1.6", "vscode-jsonrpc": "^5.0.1", - "vscode-languageclient": "^6.1.3", + "vscode-languageclient": "^8.0.2", "vscode-test-adapter-api": "~1.7.0", "vscode-test-adapter-util": "~0.7.0", "zip-a-folder": "~1.1.3" @@ -40217,38 +40217,39 @@ } }, "node_modules/vscode-languageclient": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.1.3.tgz", - "integrity": "sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.2.tgz", + "integrity": "sha512-lHlthJtphG9gibGb/y72CKqQUxwPsMXijJVpHEC2bvbFqxmkj9LwQ3aGU9dwjBLqsX1S4KjShYppLvg1UJDF/Q==", "dependencies": { - "semver": "^6.3.0", - "vscode-languageserver-protocol": "^3.15.3" + "minimatch": "^3.0.4", + "semver": "^7.3.5", + "vscode-languageserver-protocol": "3.17.2" }, "engines": { - "vscode": "^1.41.0" - } - }, - "node_modules/vscode-languageclient/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" + "vscode": "^1.67.0" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.15.3", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz", - "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==", + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", + "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", "dependencies": { - "vscode-jsonrpc": "^5.0.1", - "vscode-languageserver-types": "3.15.1" + "vscode-jsonrpc": "8.0.2", + "vscode-languageserver-types": "3.17.2" + } + }, + "node_modules/vscode-languageserver-protocol/node_modules/vscode-jsonrpc": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", + "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==", + "engines": { + "node": ">=14.0.0" } }, "node_modules/vscode-languageserver-types": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", - "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", + "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==" }, "node_modules/vscode-test": { "version": "1.6.1", @@ -72385,34 +72386,35 @@ "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==" }, "vscode-languageclient": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.1.3.tgz", - "integrity": "sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.2.tgz", + "integrity": "sha512-lHlthJtphG9gibGb/y72CKqQUxwPsMXijJVpHEC2bvbFqxmkj9LwQ3aGU9dwjBLqsX1S4KjShYppLvg1UJDF/Q==", "requires": { - "semver": "^6.3.0", - "vscode-languageserver-protocol": "^3.15.3" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } + "minimatch": "^3.0.4", + "semver": "^7.3.5", + "vscode-languageserver-protocol": "3.17.2" } }, "vscode-languageserver-protocol": { - "version": "3.15.3", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz", - "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==", + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", + "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", "requires": { - "vscode-jsonrpc": "^5.0.1", - "vscode-languageserver-types": "3.15.1" + "vscode-jsonrpc": "8.0.2", + "vscode-languageserver-types": "3.17.2" + }, + "dependencies": { + "vscode-jsonrpc": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", + "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==" + } } }, "vscode-languageserver-types": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", - "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", + "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==" }, "vscode-test": { "version": "1.6.1", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index b5a0da12e..87690f1a1 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -13,7 +13,7 @@ "url": "https://github.com/github/vscode-codeql" }, "engines": { - "vscode": "^1.59.0", + "vscode": "^1.67.0", "node": "^16.13.0", "npm": ">=7.20.6" }, @@ -1296,7 +1296,7 @@ "unzipper": "~0.10.5", "vscode-extension-telemetry": "^0.1.6", "vscode-jsonrpc": "^5.0.1", - "vscode-languageclient": "^6.1.3", + "vscode-languageclient": "^8.0.2", "vscode-test-adapter-api": "~1.7.0", "vscode-test-adapter-util": "~0.7.0", "zip-a-folder": "~1.1.3" diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index facd38f67..192a02e7f 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -18,7 +18,7 @@ import { workspace, ProviderResult } from 'vscode'; -import { LanguageClient } from 'vscode-languageclient'; +import { LanguageClient } from 'vscode-languageclient/node'; import * as os from 'os'; import * as fs from 'fs-extra'; import * as path from 'path'; @@ -1130,8 +1130,10 @@ async function activateWithInstalledDistribution( ctx.subscriptions.push(new SummaryLanguageSupport()); void logger.log('Starting language server.'); - ctx.subscriptions.push(client.start()); - + await client.start(); + ctx.subscriptions.push({ + dispose: () => { void client.stop(); } + }); // Jump-to-definition and find-references void logger.log('Registering jump-to-definition handlers.'); diff --git a/extensions/ql-vscode/src/ide-server.ts b/extensions/ql-vscode/src/ide-server.ts index c148e6f7e..778818ce1 100644 --- a/extensions/ql-vscode/src/ide-server.ts +++ b/extensions/ql-vscode/src/ide-server.ts @@ -1,5 +1,5 @@ import { ProgressLocation, window } from 'vscode'; -import { StreamInfo } from 'vscode-languageclient'; +import { StreamInfo } from 'vscode-languageclient/node'; import * as cli from './cli'; import { QueryServerConfig } from './config'; import { ideServerLogger } from './logging'; diff --git a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts index 4f95933ee..88253802f 100644 --- a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts +++ b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts @@ -6,7 +6,7 @@ import { CancellationToken, Uri, } from 'vscode'; -import { ErrorCodes, ResponseError } from 'vscode-languageclient'; +import { LSPErrorCodes, ResponseError } from 'vscode-languageclient'; import * as cli from '../cli'; import { DatabaseItem, } from '../databases'; @@ -400,7 +400,7 @@ export async function compileAndRunQueryAgainstDatabase( try { errors = await query.compile(qs, qlProgram, progress, token); } catch (e) { - if (e instanceof ResponseError && e.code == ErrorCodes.RequestCancelled) { + if (e instanceof ResponseError && e.code == LSPErrorCodes.RequestCancelled) { return createSyntheticResult(query, 'Query cancelled'); } else { throw e; diff --git a/extensions/ql-vscode/src/quick-query.ts b/extensions/ql-vscode/src/quick-query.ts index 8a30c7dd8..60f245224 100644 --- a/extensions/ql-vscode/src/quick-query.ts +++ b/extensions/ql-vscode/src/quick-query.ts @@ -8,7 +8,7 @@ import { workspace, Uri } from 'vscode'; -import { ErrorCodes, ResponseError } from 'vscode-languageclient'; +import { LSPErrorCodes, ResponseError } from 'vscode-languageclient'; import { CodeQLCliServer } from './cli'; import { DatabaseUI } from './databases-ui'; import { @@ -139,7 +139,7 @@ export async function displayQuickQuery( await Window.showTextDocument(await workspace.openTextDocument(qlFile)); } catch (e) { - if (e instanceof ResponseError && e.code == ErrorCodes.RequestCancelled) { + if (e instanceof ResponseError && e.code == LSPErrorCodes.RequestCancelled) { throw new UserCancellationException(getErrorMessage(e)); } else { throw e; From df832746ade6cc481c04e9672846faf5aeb50f93 Mon Sep 17 00:00:00 2001 From: Alexander Eyers-Taylor Date: Wed, 26 Oct 2022 17:09:56 +0000 Subject: [PATCH 02/58] Also upgrade jsonrpc --- extensions/ql-vscode/package-lock.json | 33 +++++-------------- extensions/ql-vscode/package.json | 2 +- .../legacy-query-server/queryserver-client.ts | 4 +-- .../ql-vscode/src/pure/legacy-messages.ts | 28 ++++++++-------- .../ql-vscode/src/pure/messages-shared.ts | 2 +- extensions/ql-vscode/src/pure/new-messages.ts | 11 +++---- .../src/query-server/queryserver-client.ts | 4 +-- 7 files changed, 32 insertions(+), 52 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 22d294dda..6767c2b0d 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -42,7 +42,7 @@ "tree-kill": "~1.2.2", "unzipper": "~0.10.5", "vscode-extension-telemetry": "^0.1.6", - "vscode-jsonrpc": "^5.0.1", + "vscode-jsonrpc": "^8.0.2", "vscode-languageclient": "^8.0.2", "vscode-test-adapter-api": "~1.7.0", "vscode-test-adapter-util": "~0.7.0", @@ -149,7 +149,7 @@ "engines": { "node": "^16.13.0", "npm": ">=7.20.6", - "vscode": "^1.59.0" + "vscode": "^1.67.0" } }, "node_modules/@adobe/css-tools": { @@ -40209,11 +40209,11 @@ } }, "node_modules/vscode-jsonrpc": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz", - "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", + "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==", "engines": { - "node": ">=8.0.0 || >=10.0.0" + "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { @@ -40238,14 +40238,6 @@ "vscode-languageserver-types": "3.17.2" } }, - "node_modules/vscode-languageserver-protocol/node_modules/vscode-jsonrpc": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", - "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/vscode-languageserver-types": { "version": "3.17.2", "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", @@ -72381,9 +72373,9 @@ } }, "vscode-jsonrpc": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz", - "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==" + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", + "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==" }, "vscode-languageclient": { "version": "8.0.2", @@ -72402,13 +72394,6 @@ "requires": { "vscode-jsonrpc": "8.0.2", "vscode-languageserver-types": "3.17.2" - }, - "dependencies": { - "vscode-jsonrpc": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", - "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==" - } } }, "vscode-languageserver-types": { diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 87690f1a1..177d4894e 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1295,7 +1295,7 @@ "tree-kill": "~1.2.2", "unzipper": "~0.10.5", "vscode-extension-telemetry": "^0.1.6", - "vscode-jsonrpc": "^5.0.1", + "vscode-jsonrpc": "^8.0.2", "vscode-languageclient": "^8.0.2", "vscode-test-adapter-api": "~1.7.0", "vscode-test-adapter-util": "~0.7.0", diff --git a/extensions/ql-vscode/src/legacy-query-server/queryserver-client.ts b/extensions/ql-vscode/src/legacy-query-server/queryserver-client.ts index fded7aad2..3afc3dc66 100644 --- a/extensions/ql-vscode/src/legacy-query-server/queryserver-client.ts +++ b/extensions/ql-vscode/src/legacy-query-server/queryserver-client.ts @@ -3,7 +3,7 @@ import * as fs from 'fs-extra'; import { DisposableObject } from '../pure/disposable-object'; import { CancellationToken, commands } from 'vscode'; -import { createMessageConnection, RequestType } from 'vscode-jsonrpc'; +import { createMessageConnection, RequestType } from 'vscode-jsonrpc/node'; import * as cli from '../cli'; import { QueryServerConfig } from '../config'; import { Logger, ProgressReporter } from '../logging'; @@ -201,7 +201,7 @@ export class QueryServerClient extends DisposableObject { return this.serverProcess!.child.pid || 0; } - async sendRequest(type: RequestType, R, E, RO>, parameter: P, token?: CancellationToken, progress?: (res: ProgressMessage) => void): Promise { + async sendRequest(type: RequestType, R, E>, parameter: P, token?: CancellationToken, progress?: (res: ProgressMessage) => void): Promise { const id = this.nextProgress++; this.progressCallbacks[id] = progress; diff --git a/extensions/ql-vscode/src/pure/legacy-messages.ts b/extensions/ql-vscode/src/pure/legacy-messages.ts index b8ac43765..dbe464436 100644 --- a/extensions/ql-vscode/src/pure/legacy-messages.ts +++ b/extensions/ql-vscode/src/pure/legacy-messages.ts @@ -977,70 +977,68 @@ export type ProgressMessage = shared.ProgressMessage; /** * Check a Ql query for errors without compiling it */ -export const checkQuery = new rpc.RequestType, CheckQueryResult, void, void>('compilation/checkQuery'); +export const checkQuery = new rpc.RequestType, CheckQueryResult, void>('compilation/checkQuery'); /** * Compile a Ql query into a qlo */ -export const compileQuery = new rpc.RequestType, CheckQueryResult, void, void>('compilation/compileQuery'); +export const compileQuery = new rpc.RequestType, CheckQueryResult, void>('compilation/compileQuery'); /** * Compile a dil query into a qlo */ -export const compileDilQuery = new rpc.RequestType, CheckQueryResult, void, void>('compilation/compileDilQuery'); +export const compileDilQuery = new rpc.RequestType, CheckQueryResult, void>('compilation/compileDilQuery'); /** * Check if there is a valid upgrade path between two dbschemes. */ -export const checkUpgrade = new rpc.RequestType, CheckUpgradeResult, void, void>('compilation/checkUpgrade'); +export const checkUpgrade = new rpc.RequestType, CheckUpgradeResult, void>('compilation/checkUpgrade'); /** * Compile an upgrade script to upgrade a dataset. */ -export const compileUpgrade = new rpc.RequestType, CompileUpgradeResult, void, void>('compilation/compileUpgrade'); +export const compileUpgrade = new rpc.RequestType, CompileUpgradeResult, void>('compilation/compileUpgrade'); /** * Compile an upgrade script to upgrade a dataset. */ -export const compileUpgradeSequence = new rpc.RequestType, CompileUpgradeSequenceResult, void, void>('compilation/compileUpgradeSequence'); +export const compileUpgradeSequence = new rpc.RequestType, CompileUpgradeSequenceResult, void>('compilation/compileUpgradeSequence'); /** * Start a new structured log in the evaluator, terminating the previous one if it exists */ -export const startLog = new rpc.RequestType, StartLogResult, void, void>('evaluation/startLog'); +export const startLog = new rpc.RequestType, StartLogResult, void>('evaluation/startLog'); /** * Terminate a structured log in the evaluator. Is a no-op if we aren't logging to the given location */ -export const endLog = new rpc.RequestType, EndLogResult, void, void>('evaluation/endLog'); +export const endLog = new rpc.RequestType, EndLogResult, void>('evaluation/endLog'); /** * Clear the cache of a dataset */ -export const clearCache = new rpc.RequestType, ClearCacheResult, void, void>('evaluation/clearCache'); +export const clearCache = new rpc.RequestType, ClearCacheResult, void>('evaluation/clearCache'); /** * Trim the cache of a dataset */ -export const trimCache = new rpc.RequestType, ClearCacheResult, void, void>('evaluation/trimCache'); +export const trimCache = new rpc.RequestType, ClearCacheResult, void>('evaluation/trimCache'); /** * Run some queries on a dataset */ -export const runQueries = new rpc.RequestType, EvaluationComplete, void, void>('evaluation/runQueries'); +export const runQueries = new rpc.RequestType, EvaluationComplete, void>('evaluation/runQueries'); /** * Run upgrades on a dataset */ -export const runUpgrade = new rpc.RequestType, RunUpgradeResult, void, void>('evaluation/runUpgrade'); +export const runUpgrade = new rpc.RequestType, RunUpgradeResult, void>('evaluation/runUpgrade'); export const registerDatabases = new rpc.RequestType< WithProgressId, RegisterDatabasesResult, - void, void >('evaluation/registerDatabases'); export const deregisterDatabases = new rpc.RequestType< WithProgressId, DeregisterDatabasesResult, - void, void >('evaluation/deregisterDatabases'); @@ -1048,6 +1046,6 @@ export const deregisterDatabases = new rpc.RequestType< * Request returned to the client to notify completion of a query. * The full runQueries job is completed when all queries are acknowledged. */ -export const completeQuery = new rpc.RequestType, void, void>('evaluation/queryCompleted'); +export const completeQuery = new rpc.RequestType, void>('evaluation/queryCompleted'); export const progress = shared.progress; diff --git a/extensions/ql-vscode/src/pure/messages-shared.ts b/extensions/ql-vscode/src/pure/messages-shared.ts index ee7b04240..07b4fc260 100644 --- a/extensions/ql-vscode/src/pure/messages-shared.ts +++ b/extensions/ql-vscode/src/pure/messages-shared.ts @@ -107,4 +107,4 @@ export interface ProgressMessage { /** * A notification that the progress has been changed. */ -export const progress = new rpc.NotificationType('ql/progressUpdated'); +export const progress = new rpc.NotificationType('ql/progressUpdated'); diff --git a/extensions/ql-vscode/src/pure/new-messages.ts b/extensions/ql-vscode/src/pure/new-messages.ts index 7b99d33ca..7e5257262 100644 --- a/extensions/ql-vscode/src/pure/new-messages.ts +++ b/extensions/ql-vscode/src/pure/new-messages.ts @@ -171,33 +171,31 @@ export type ProgressMessage = shared.ProgressMessage; /** * Clear the cache of a dataset */ -export const clearCache = new rpc.RequestType, ClearCacheResult, void, void>('evaluation/clearCache'); +export const clearCache = new rpc.RequestType, ClearCacheResult, void>('evaluation/clearCache'); /** * Trim the cache of a dataset */ -export const trimCache = new rpc.RequestType, ClearCacheResult, void, void>('evaluation/trimCache'); +export const trimCache = new rpc.RequestType, ClearCacheResult, void>('evaluation/trimCache'); /** * Clear the pack cache */ -export const clearPackCache = new rpc.RequestType, ClearPackCacheResult, void, void>('evaluation/clearPackCache'); +export const clearPackCache = new rpc.RequestType, ClearPackCacheResult, void>('evaluation/clearPackCache'); /** * Run a query on a database */ -export const runQuery = new rpc.RequestType, RunQueryResult, void, void>('evaluation/runQuery'); +export const runQuery = new rpc.RequestType, RunQueryResult, void>('evaluation/runQuery'); export const registerDatabases = new rpc.RequestType< WithProgressId, RegisterDatabasesResult, - void, void >('evaluation/registerDatabases'); export const deregisterDatabases = new rpc.RequestType< WithProgressId, DeregisterDatabasesResult, - void, void >('evaluation/deregisterDatabases'); @@ -205,7 +203,6 @@ export const deregisterDatabases = new rpc.RequestType< export const upgradeDatabase = new rpc.RequestType< WithProgressId, UpgradeResult, - void, void >('evaluation/runUpgrade'); diff --git a/extensions/ql-vscode/src/query-server/queryserver-client.ts b/extensions/ql-vscode/src/query-server/queryserver-client.ts index 7ee5ac275..f562645e1 100644 --- a/extensions/ql-vscode/src/query-server/queryserver-client.ts +++ b/extensions/ql-vscode/src/query-server/queryserver-client.ts @@ -3,7 +3,7 @@ import * as fs from 'fs-extra'; import { DisposableObject } from '../pure/disposable-object'; import { CancellationToken, commands } from 'vscode'; -import { createMessageConnection, RequestType } from 'vscode-jsonrpc'; +import { createMessageConnection, RequestType } from 'vscode-jsonrpc/node'; import * as cli from '../cli'; import { QueryServerConfig } from '../config'; import { Logger, ProgressReporter } from '../logging'; @@ -174,7 +174,7 @@ export class QueryServerClient extends DisposableObject { return this.serverProcess!.child.pid || 0; } - async sendRequest(type: RequestType, R, E, RO>, parameter: P, token?: CancellationToken, progress?: (res: ProgressMessage) => void): Promise { + async sendRequest(type: RequestType, R, E>, parameter: P, token?: CancellationToken, progress?: (res: ProgressMessage) => void): Promise { const id = this.nextProgress++; this.progressCallbacks[id] = progress; From a0ab34bf55ff47be601f6d7f3d594b62f33712ca Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 22 Nov 2022 15:51:56 +0100 Subject: [PATCH 03/58] 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). --- .../ql-vscode/jest-runner-vscode.config.js | 18 + extensions/ql-vscode/jest.config.js | 6 +- extensions/ql-vscode/package.json | 4 +- .../local-queries/local-query-history-item.ts | 13 +- .../src/vscode-tests/jest.config.base.ts | 205 +++++++ .../ql-vscode/src/vscode-tests/jest.setup.ts | 9 + .../{ => activation}/activation.test.ts | 8 +- .../archive-filesystem-provider.test.ts | 37 +- .../no-workspace/astViewer.test.ts | 31 +- .../contextual/astBuilder.test.ts | 34 +- .../contextual/fileRangeFromURI.test.ts | 16 +- .../contextual/queryResolver.test.ts | 145 ++--- .../no-workspace/databaseFetcher.test.ts | 183 +++---- .../no-workspace/databases-ui.test.ts | 21 +- .../no-workspace/distribution.test.ts | 150 +++--- .../no-workspace/download-link.test.ts | 6 +- .../eval-log-tree-builder.test.ts | 13 +- .../no-workspace/eval-log-viewer.test.ts | 20 +- .../vscode-tests/no-workspace/helpers.test.ts | 214 ++++---- .../history-item-label-provider.test.ts | 47 +- .../src/vscode-tests/no-workspace/index.ts | 14 - .../no-workspace/interface-utils.test.ts | 82 +-- .../vscode-tests/no-workspace/jest.config.ts | 9 + .../no-workspace/query-history-info.test.ts | 33 +- .../no-workspace/query-history.test.ts | 499 ++++++++++-------- .../no-workspace/query-results.test.ts | 123 ++--- .../remote-queries/export-results.test.ts | 52 +- .../gh-api/gh-actions-api-client.test.ts | 72 ++- .../remote-queries/remote-queries-api.test.ts | 33 +- .../remote-query-history.test.ts | 250 +++++---- .../repository-selection.test.ts | 264 ++++----- .../variant-analysis-history.test.ts | 135 ++--- .../no-workspace/run-queries.test.ts | 108 ++-- .../no-workspace/sarifParser.test.ts | 19 +- .../no-workspace/telemetry.test.ts | 164 +++--- .../no-workspace/test-adapter.test.ts | 163 +++--- 36 files changed, 1683 insertions(+), 1517 deletions(-) create mode 100644 extensions/ql-vscode/jest-runner-vscode.config.js create mode 100644 extensions/ql-vscode/src/vscode-tests/jest.config.base.ts create mode 100644 extensions/ql-vscode/src/vscode-tests/jest.setup.ts rename extensions/ql-vscode/src/vscode-tests/no-workspace/{ => activation}/activation.test.ts (61%) create mode 100644 extensions/ql-vscode/src/vscode-tests/no-workspace/jest.config.ts diff --git a/extensions/ql-vscode/jest-runner-vscode.config.js b/extensions/ql-vscode/jest-runner-vscode.config.js new file mode 100644 index 000000000..735c40f6c --- /dev/null +++ b/extensions/ql-vscode/jest-runner-vscode.config.js @@ -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), +}; diff --git a/extensions/ql-vscode/jest.config.js b/extensions/ql-vscode/jest.config.js index 6ecdb33e3..9c35edfdd 100644 --- a/extensions/ql-vscode/jest.config.js +++ b/extensions/ql-vscode/jest.config.js @@ -4,5 +4,9 @@ */ module.exports = { - projects: ["/src/view", "/test"], + projects: [ + "/src/view", + "/test", + "/out/vscode-tests/no-workspace", + ], }; diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index b79488e75..5e372b3d6 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -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", diff --git a/extensions/ql-vscode/src/vscode-tests/factories/local-queries/local-query-history-item.ts b/extensions/ql-vscode/src/vscode-tests/factories/local-queries/local-query-history-item.ts index 87a342274..12db83a64 100644 --- a/extensions/ql-vscode/src/vscode-tests/factories/local-queries/local-query-history-item.ts +++ b/extensions/ql-vscode/src/vscode-tests/factories/local-queries/local-query-history-item.ts @@ -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; diff --git a/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts b/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts new file mode 100644 index 000000000..6a4c0d964 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts @@ -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: [ + // "" + // ], + + // 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: ["/../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; diff --git a/extensions/ql-vscode/src/vscode-tests/jest.setup.ts b/extensions/ql-vscode/src/vscode-tests/jest.setup.ts new file mode 100644 index 000000000..fb764fb45 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/jest.setup.ts @@ -0,0 +1,9 @@ +import { env } from "vscode"; + +(env as any).openExternal = () => { + /**/ +}; + +afterAll(() => { + jest.restoreAllMocks(); +}); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/activation.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/activation/activation.test.ts similarity index 61% rename from extensions/ql-vscode/src/vscode-tests/no-workspace/activation.test.ts rename to extensions/ql-vscode/src/vscode-tests/no-workspace/activation/activation.test.ts index 34d9c8f84..8019f26de 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/activation.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/activation/activation.test.ts @@ -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(); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/archive-filesystem-provider.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/archive-filesystem-provider.test.ts index e233fdf5a..5a16c3ef1 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/archive-filesystem-provider.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/archive-filesystem-provider.test.ts @@ -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: "/", }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/astViewer.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/astViewer.test.ts index 72df811fa..36b10683f 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/astViewer.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/astViewer.test.ts @@ -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(); } } diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/astBuilder.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/astBuilder.test.ts index 5b2fac7f6..6be6ded9e 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/astBuilder.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/astBuilder.test.ts @@ -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() { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/fileRangeFromURI.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/fileRangeFromURI.test.ts index a60de9b3a..5a4607fda 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/fileRangeFromURI.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/fileRangeFromURI.test.ts @@ -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), }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts index 5c6cf3854..bdb1710aa 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts @@ -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; - let writeFileSpy: sinon.SinonSpy; - let getQlPackForDbschemeSpy: sinon.SinonStub; - let getPrimaryDbschemeSpy: sinon.SinonStub; - let mockCli: Record< - string, - sinon.SinonStub | Record - >; + 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: () => ({}), - }, - }); - } }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts index 5bb6caaaa..a45f64263 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts @@ -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[]) { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/databases-ui.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/databases-ui.test.ts index 4886f1445..82d6c22f8 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/databases-ui.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/databases-ui.test.ts @@ -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); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts index 7a89032d7..26c7421af 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts @@ -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, - }, - }); - } }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/download-link.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/download-link.test.ts index cfbb11d72..afcf8d3f5 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/download-link.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/download-link.test.ts @@ -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); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/eval-log-tree-builder.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/eval-log-tree-builder.test.ts index 251de77ca..9fe38eacd 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/eval-log-tree-builder.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/eval-log-tree-builder.test.ts @@ -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); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/eval-log-viewer.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/eval-log-viewer.test.ts index 9a1eca9d5..c1f0825d6 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/eval-log-viewer.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/eval-log-viewer.test.ts @@ -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); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts index 6635474c9..1020e41ed 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts @@ -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]); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/history-item-label-provider.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/history-item-label-provider.test.ts index d45380262..57947be09 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/history-item-label-provider.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/history-item-label-provider.test.ts @@ -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 % `, ); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/index.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/index.ts index 1f66876ba..fd5efce3f 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/index.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/index.ts @@ -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 { - return runTestsInDirectory(__dirname); -} - export function createMockExtensionContext(): ExtensionContext { return { globalState: { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/interface-utils.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/interface-utils.test.ts index 89cca85e4..783d445b5 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/interface-utils.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/interface-utils.test.ts @@ -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 = ''; @@ -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(); }); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/jest.config.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest.config.ts new file mode 100644 index 000000000..bce274e27 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest.config.ts @@ -0,0 +1,9 @@ +import type { Config } from "jest"; + +import baseConfig from "../jest.config.base"; + +const config: Config = { + ...baseConfig, +}; + +export default config; diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history-info.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history-info.test.ts index 7d30f63f3..212873501 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history-info.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history-info.test.ts @@ -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}`, ); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts index 1322f8156..bf271ba1b 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts @@ -1,8 +1,6 @@ import * as fs from "fs-extra"; import * as path from "path"; -import { assert, expect } from "chai"; import * as vscode from "vscode"; -import * as sinon from "sinon"; import { logger } from "../../logging"; import { registerQueryHistoryScrubber } from "../../query-history-scrubber"; @@ -43,6 +41,8 @@ import { QueryStatus } from "../../query-status"; import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis"; import * as ghActionsApiClient from "../../remote-queries/gh-api/gh-actions-api-client"; import { Credentials } from "../../authentication"; +import { QuickPickItem, TextEditor } from "vscode"; +import { WebviewReveal } from "../../interface-utils"; describe("query-history", () => { const mockExtensionLocation = path.join( @@ -50,19 +50,23 @@ describe("query-history", () => { "mock-extension-location", ); let configListener: QueryHistoryConfigListener; - let showTextDocumentSpy: sinon.SinonStub; - let showInformationMessageSpy: sinon.SinonStub; - let executeCommandSpy: sinon.SinonStub; - let showQuickPickSpy: sinon.SinonStub; + const showTextDocumentSpy = jest.spyOn(vscode.window, "showTextDocument"); + const showInformationMessageSpy = jest.spyOn( + vscode.window, + "showInformationMessage", + ); + const showQuickPickSpy = jest.spyOn(vscode.window, "showQuickPick"); + const executeCommandSpy = jest.spyOn(vscode.commands, "executeCommand"); + const logSpy = jest.spyOn(logger, "log"); + const doCompareCallback = jest.fn(); + let queryHistoryManager: QueryHistoryManager | undefined; - let doCompareCallback: sinon.SinonStub; let localQueriesResultsViewStub: ResultsView; let remoteQueriesManagerStub: RemoteQueriesManager; let variantAnalysisManagerStub: VariantAnalysisManager; let tryOpenExternalFile: Function; - let sandbox: sinon.SinonSandbox; let allHistory: QueryHistoryInfo[]; let localQueryHistory: LocalQueryInfo[]; @@ -70,37 +74,37 @@ describe("query-history", () => { let variantAnalysisHistory: VariantAnalysisHistoryItem[]; beforeEach(() => { - sandbox = sinon.createSandbox(); + showTextDocumentSpy + .mockClear() + .mockResolvedValue(undefined as unknown as TextEditor); + + showInformationMessageSpy.mockClear().mockResolvedValue(undefined); + showQuickPickSpy.mockClear().mockResolvedValue(undefined); + executeCommandSpy.mockClear().mockResolvedValue(undefined); + logSpy.mockClear().mockResolvedValue(undefined); + + doCompareCallback.mockReset(); - showTextDocumentSpy = sandbox.stub(vscode.window, "showTextDocument"); - showInformationMessageSpy = sandbox.stub( - vscode.window, - "showInformationMessage", - ); - showQuickPickSpy = sandbox.stub(vscode.window, "showQuickPick"); - executeCommandSpy = sandbox.stub(vscode.commands, "executeCommand"); - sandbox.stub(logger, "log"); tryOpenExternalFile = (QueryHistoryManager.prototype as any) .tryOpenExternalFile; configListener = new QueryHistoryConfigListener(); - doCompareCallback = sandbox.stub(); localQueriesResultsViewStub = { - showResults: sandbox.stub(), + showResults: jest.fn(), } as any as ResultsView; remoteQueriesManagerStub = { - onRemoteQueryAdded: sandbox.stub(), - onRemoteQueryRemoved: sandbox.stub(), - onRemoteQueryStatusUpdate: sandbox.stub(), - removeRemoteQuery: sandbox.stub(), - openRemoteQueryResults: sandbox.stub(), + onRemoteQueryAdded: jest.fn(), + onRemoteQueryRemoved: jest.fn(), + onRemoteQueryStatusUpdate: jest.fn(), + removeRemoteQuery: jest.fn(), + openRemoteQueryResults: jest.fn(), } as any as RemoteQueriesManager; variantAnalysisManagerStub = { - onVariantAnalysisAdded: sandbox.stub(), - onVariantAnalysisStatusUpdated: sandbox.stub(), - onVariantAnalysisRemoved: sandbox.stub(), - removeVariantAnalysis: sandbox.stub(), - showView: sandbox.stub(), + onVariantAnalysisAdded: jest.fn(), + onVariantAnalysisStatusUpdated: jest.fn(), + onVariantAnalysisRemoved: jest.fn(), + removeVariantAnalysis: jest.fn(), + showView: jest.fn(), } as any as VariantAnalysisManager; localQueryHistory = [ @@ -108,7 +112,6 @@ describe("query-history", () => { createMockLocalQueryInfo({ dbName: "a", queryWithResults: createMockQueryWithResults({ - sandbox, didRunSuccessfully: true, }), }), @@ -116,7 +119,6 @@ describe("query-history", () => { createMockLocalQueryInfo({ dbName: "b", queryWithResults: createMockQueryWithResults({ - sandbox, didRunSuccessfully: true, }), }), @@ -124,7 +126,6 @@ describe("query-history", () => { createMockLocalQueryInfo({ dbName: "a", queryWithResults: createMockQueryWithResults({ - sandbox, didRunSuccessfully: false, }), }), @@ -132,7 +133,6 @@ describe("query-history", () => { createMockLocalQueryInfo({ dbName: "a", queryWithResults: createMockQueryWithResults({ - sandbox, didRunSuccessfully: true, }), }), @@ -177,17 +177,18 @@ describe("query-history", () => { queryHistoryManager.dispose(); queryHistoryManager = undefined; } - sandbox.restore(); }); describe("QueryHistoryManager", () => { describe("tryOpenExternalFile", () => { it("should open an external file", async () => { await tryOpenExternalFile("xxx"); - expect(showTextDocumentSpy).to.have.been.calledOnceWith( + expect(showTextDocumentSpy).toHaveBeenCalledTimes(1); + expect(showTextDocumentSpy).toHaveBeenCalledWith( vscode.Uri.file("xxx"), + expect.anything(), ); - expect(executeCommandSpy).not.to.have.been.called; + expect(executeCommandSpy).not.toBeCalled(); }); [ @@ -195,35 +196,40 @@ describe("query-history", () => { "Files above 50MB cannot be synchronized with extensions", ].forEach((msg) => { it(`should fail to open a file because "${msg}" and open externally`, async () => { - showTextDocumentSpy.throws(new Error(msg)); - showInformationMessageSpy.returns({ title: "Yes" }); + showTextDocumentSpy.mockRejectedValue(new Error(msg)); + showInformationMessageSpy.mockResolvedValue({ title: "Yes" }); await tryOpenExternalFile("xxx"); const uri = vscode.Uri.file("xxx"); - expect(showTextDocumentSpy).to.have.been.calledOnceWith(uri); - expect(executeCommandSpy).to.have.been.calledOnceWith( - "revealFileInOS", + expect(showTextDocumentSpy).toHaveBeenCalledTimes(1); + expect(showTextDocumentSpy).toHaveBeenCalledWith( uri, + expect.anything(), ); + expect(executeCommandSpy).toHaveBeenCalledWith("revealFileInOS", uri); }); it(`should fail to open a file because "${msg}" and NOT open externally`, async () => { - showTextDocumentSpy.throws(new Error(msg)); - showInformationMessageSpy.returns({ title: "No" }); + showTextDocumentSpy.mockRejectedValue(new Error(msg)); + showInformationMessageSpy.mockResolvedValue({ title: "No" }); await tryOpenExternalFile("xxx"); const uri = vscode.Uri.file("xxx"); - expect(showTextDocumentSpy).to.have.been.calledOnceWith(uri); - expect(showInformationMessageSpy).to.have.been.called; - expect(executeCommandSpy).not.to.have.been.called; + expect(showTextDocumentSpy).toHaveBeenCalledTimes(1); + expect(showTextDocumentSpy).toHaveBeenCalledWith( + uri, + expect.anything(), + ); + expect(showInformationMessageSpy).toBeCalled(); + expect(executeCommandSpy).not.toBeCalled(); }); }); }); - describe("handleItemClicked", async () => { - describe("single click", async () => { - describe("local query", async () => { - describe("when complete", async () => { + describe("handleItemClicked", () => { + describe("single click", () => { + describe("local query", () => { + describe("when complete", () => { it("should show results", async () => { queryHistoryManager = await createMockQueryHistory(allHistory); const itemClicked = localQueryHistory[0]; @@ -233,14 +239,17 @@ describe("query-history", () => { expect( localQueriesResultsViewStub.showResults, - ).to.have.been.calledOnceWith(itemClicked); - expect(queryHistoryManager.treeDataProvider.getCurrent()).to.eq( + ).toHaveBeenCalledTimes(1); + expect( + localQueriesResultsViewStub.showResults, + ).toHaveBeenCalledWith(itemClicked, WebviewReveal.Forced, false); + expect(queryHistoryManager.treeDataProvider.getCurrent()).toBe( itemClicked, ); }); }); - describe("when incomplete", async () => { + describe("when incomplete", () => { it("should do nothing", async () => { queryHistoryManager = await createMockQueryHistory(allHistory); const itemClicked = localQueryHistory[2]; @@ -250,13 +259,13 @@ describe("query-history", () => { expect( localQueriesResultsViewStub.showResults, - ).not.to.have.been.calledWith(itemClicked); + ).not.toHaveBeenCalled(); }); }); }); - describe("remote query", async () => { - describe("when complete", async () => { + describe("remote query", () => { + describe("when complete", () => { it("should show results", async () => { queryHistoryManager = await createMockQueryHistory(allHistory); const itemClicked = remoteQueryHistory[0]; @@ -266,14 +275,17 @@ describe("query-history", () => { expect( remoteQueriesManagerStub.openRemoteQueryResults, - ).to.have.been.calledOnceWith(itemClicked.queryId); - expect(queryHistoryManager.treeDataProvider.getCurrent()).to.eq( + ).toHaveBeenCalledTimes(1); + expect( + remoteQueriesManagerStub.openRemoteQueryResults, + ).toHaveBeenCalledWith(itemClicked.queryId); + expect(queryHistoryManager.treeDataProvider.getCurrent()).toBe( itemClicked, ); }); }); - describe("when incomplete", async () => { + describe("when incomplete", () => { it("should do nothing", async () => { queryHistoryManager = await createMockQueryHistory(allHistory); const itemClicked = remoteQueryHistory[2]; @@ -283,13 +295,13 @@ describe("query-history", () => { expect( remoteQueriesManagerStub.openRemoteQueryResults, - ).not.to.have.been.calledWith(itemClicked.queryId); + ).not.toBeCalledWith(itemClicked.queryId); }); }); }); - describe("variant analysis", async () => { - describe("when complete", async () => { + describe("variant analysis", () => { + describe("when complete", () => { it("should show results", async () => { queryHistoryManager = await createMockQueryHistory(allHistory); const itemClicked = variantAnalysisHistory[0]; @@ -297,16 +309,19 @@ describe("query-history", () => { itemClicked, ]); - expect( - variantAnalysisManagerStub.showView, - ).to.have.been.calledOnceWith(itemClicked.variantAnalysis.id); - expect(queryHistoryManager.treeDataProvider.getCurrent()).to.eq( + expect(variantAnalysisManagerStub.showView).toHaveBeenCalledTimes( + 1, + ); + expect(variantAnalysisManagerStub.showView).toHaveBeenCalledWith( + itemClicked.variantAnalysis.id, + ); + expect(queryHistoryManager.treeDataProvider.getCurrent()).toBe( itemClicked, ); }); }); - describe("when incomplete", async () => { + describe("when incomplete", () => { it("should show results", async () => { queryHistoryManager = await createMockQueryHistory(allHistory); const itemClicked = variantAnalysisHistory[1]; @@ -314,10 +329,13 @@ describe("query-history", () => { itemClicked, ]); - expect( - variantAnalysisManagerStub.showView, - ).to.have.been.calledOnceWith(itemClicked.variantAnalysis.id); - expect(queryHistoryManager.treeDataProvider.getCurrent()).to.eq( + expect(variantAnalysisManagerStub.showView).toHaveBeenCalledTimes( + 1, + ); + expect(variantAnalysisManagerStub.showView).toHaveBeenCalledWith( + itemClicked.variantAnalysis.id, + ); + expect(queryHistoryManager.treeDataProvider.getCurrent()).toBe( itemClicked, ); }); @@ -336,13 +354,16 @@ describe("query-history", () => { secondItemClicked, ]); - expect(localQueriesResultsViewStub.showResults).not.to.have.been - .called; - expect(remoteQueriesManagerStub.openRemoteQueryResults).not.to.have - .been.called; - expect(variantAnalysisManagerStub.showView).not.to.have.been.called; - expect(queryHistoryManager.treeDataProvider.getCurrent()).to.be - .undefined; + expect( + localQueriesResultsViewStub.showResults, + ).not.toHaveBeenCalled(); + expect( + remoteQueriesManagerStub.openRemoteQueryResults, + ).not.toHaveBeenCalled(); + expect(variantAnalysisManagerStub.showView).not.toBeCalled(); + expect( + queryHistoryManager.treeDataProvider.getCurrent(), + ).toBeUndefined(); }); }); @@ -352,13 +373,16 @@ describe("query-history", () => { await queryHistoryManager.handleItemClicked(undefined!, []); - expect(localQueriesResultsViewStub.showResults).not.to.have.been - .called; - expect(remoteQueriesManagerStub.openRemoteQueryResults).not.to.have - .been.called; - expect(variantAnalysisManagerStub.showView).not.to.have.been.called; - expect(queryHistoryManager.treeDataProvider.getCurrent()).to.be - .undefined; + expect( + localQueriesResultsViewStub.showResults, + ).not.toHaveBeenCalled(); + expect( + remoteQueriesManagerStub.openRemoteQueryResults, + ).not.toHaveBeenCalled(); + expect(variantAnalysisManagerStub.showView).not.toHaveBeenCalled(); + expect( + queryHistoryManager.treeDataProvider.getCurrent(), + ).toBeUndefined(); }); }); }); @@ -380,25 +404,30 @@ describe("query-history", () => { await queryHistoryManager.treeView.reveal(selected, { select: true }); // should be selected - expect(queryHistoryManager.treeDataProvider.getCurrent()).to.deep.eq( + expect(queryHistoryManager.treeDataProvider.getCurrent()).toEqual( selected, ); // remove an item await queryHistoryManager.handleRemoveHistoryItem(toDelete, [toDelete]); - expect(toDelete.completedQuery!.dispose).to.have.been.calledOnce; - expect(queryHistoryManager.treeDataProvider.getCurrent()).to.deep.eq( + expect(toDelete.completedQuery!.dispose).toBeCalledTimes(1); + expect(queryHistoryManager.treeDataProvider.getCurrent()).toEqual( selected, ); - expect(queryHistoryManager.treeDataProvider.allHistory).not.to.contain( - toDelete, + expect(queryHistoryManager.treeDataProvider.allHistory).toEqual( + expect.not.arrayContaining([toDelete]), ); // the same item should be selected - expect( - localQueriesResultsViewStub.showResults, - ).to.have.been.calledOnceWith(selected); + expect(localQueriesResultsViewStub.showResults).toHaveBeenCalledTimes( + 1, + ); + expect(localQueriesResultsViewStub.showResults).toHaveBeenCalledWith( + selected, + WebviewReveal.Forced, + false, + ); }); it("should remove an item and select a new one", async () => { @@ -413,25 +442,34 @@ describe("query-history", () => { await queryHistoryManager.treeView.reveal(toDelete, { select: true }); await queryHistoryManager.handleRemoveHistoryItem(toDelete, [toDelete]); - expect(toDelete.completedQuery!.dispose).to.have.been.calledOnce; - expect(queryHistoryManager.treeDataProvider.getCurrent()).to.eq( + expect(toDelete.completedQuery!.dispose).toBeCalledTimes(1); + expect(queryHistoryManager.treeDataProvider.getCurrent()).toBe( newSelected, ); - expect(queryHistoryManager.treeDataProvider.allHistory).not.to.contain( - toDelete, + expect(queryHistoryManager.treeDataProvider.allHistory).toEqual( + expect.not.arrayContaining([toDelete]), ); // the current item should have been selected - expect( - localQueriesResultsViewStub.showResults, - ).to.have.been.calledOnceWith(newSelected); + expect(localQueriesResultsViewStub.showResults).toHaveBeenCalledTimes( + 1, + ); + expect(localQueriesResultsViewStub.showResults).toHaveBeenCalledWith( + newSelected, + WebviewReveal.Forced, + false, + ); }); }); describe("handleCancel", () => { let mockCredentials: Credentials; - let mockCancelRemoteQuery: sinon.SinonStub; - let getOctokitStub: sinon.SinonStub; + const credentialsSpy = jest.spyOn(Credentials, "initialize"); + const mockCancelRemoteQuery = jest.spyOn( + ghActionsApiClient, + "cancelRemoteQuery", + ); + const getOctokitStub = jest.fn(); beforeEach(async () => { mockCredentials = { @@ -440,23 +478,22 @@ describe("query-history", () => { request: getOctokitStub, }), } as unknown as Credentials; - sandbox.stub(Credentials, "initialize").resolves(mockCredentials); - mockCancelRemoteQuery = sandbox.stub( - ghActionsApiClient, - "cancelRemoteQuery", - ); + credentialsSpy.mockReset().mockResolvedValue(mockCredentials); + + mockCancelRemoteQuery.mockClear().mockResolvedValue(); + getOctokitStub.mockClear(); }); - describe("if the item is in progress", async () => { + describe("if the item is in progress", () => { it("should cancel a single local query", async () => { queryHistoryManager = await createMockQueryHistory(localQueryHistory); // cancelling the selected item const inProgress1 = localQueryHistory[4]; - const cancelSpy = sandbox.spy(inProgress1, "cancel"); + const cancelSpy = jest.spyOn(inProgress1, "cancel"); await queryHistoryManager.handleCancel(inProgress1, [inProgress1]); - expect(cancelSpy).to.have.been.calledOnce; + expect(cancelSpy).toBeCalledTimes(1); }); it("should cancel multiple local queries", async () => { @@ -466,15 +503,15 @@ describe("query-history", () => { const inProgress1 = localQueryHistory[4]; const inProgress2 = localQueryHistory[5]; - const cancelSpy1 = sandbox.spy(inProgress1, "cancel"); - const cancelSpy2 = sandbox.spy(inProgress2, "cancel"); + const cancelSpy1 = jest.spyOn(inProgress1, "cancel"); + const cancelSpy2 = jest.spyOn(inProgress2, "cancel"); await queryHistoryManager.handleCancel(inProgress1, [ inProgress1, inProgress2, ]); - expect(cancelSpy1).to.have.been.called; - expect(cancelSpy2).to.have.been.called; + expect(cancelSpy1).toBeCalled(); + expect(cancelSpy2).toBeCalled(); }); it("should cancel a single remote query", async () => { @@ -484,7 +521,7 @@ describe("query-history", () => { const inProgress1 = remoteQueryHistory[2]; await queryHistoryManager.handleCancel(inProgress1, [inProgress1]); - expect(mockCancelRemoteQuery).to.have.been.calledWith( + expect(mockCancelRemoteQuery).toBeCalledWith( mockCredentials, inProgress1.remoteQuery, ); @@ -501,11 +538,11 @@ describe("query-history", () => { inProgress1, inProgress2, ]); - expect(mockCancelRemoteQuery).to.have.been.calledWith( + expect(mockCancelRemoteQuery).toBeCalledWith( mockCredentials, inProgress1.remoteQuery, ); - expect(mockCancelRemoteQuery).to.have.been.calledWith( + expect(mockCancelRemoteQuery).toBeCalledWith( mockCredentials, inProgress2.remoteQuery, ); @@ -518,7 +555,7 @@ describe("query-history", () => { const inProgress1 = variantAnalysisHistory[1]; await queryHistoryManager.handleCancel(inProgress1, [inProgress1]); - expect(executeCommandSpy).to.have.been.calledWith( + expect(executeCommandSpy).toBeCalledWith( "codeQL.cancelVariantAnalysis", inProgress1.variantAnalysis.id, ); @@ -535,27 +572,27 @@ describe("query-history", () => { inProgress1, inProgress2, ]); - expect(executeCommandSpy).to.have.been.calledWith( + expect(executeCommandSpy).toBeCalledWith( "codeQL.cancelVariantAnalysis", inProgress1.variantAnalysis.id, ); - expect(executeCommandSpy).to.have.been.calledWith( + expect(executeCommandSpy).toBeCalledWith( "codeQL.cancelVariantAnalysis", inProgress2.variantAnalysis.id, ); }); }); - describe("if the item is not in progress", async () => { + describe("if the item is not in progress", () => { it("should not cancel a single local query", async () => { queryHistoryManager = await createMockQueryHistory(localQueryHistory); // cancelling the selected item const completed = localQueryHistory[0]; - const cancelSpy = sandbox.spy(completed, "cancel"); + const cancelSpy = jest.spyOn(completed, "cancel"); await queryHistoryManager.handleCancel(completed, [completed]); - expect(cancelSpy).to.not.have.been.calledOnce; + expect(cancelSpy).not.toBeCalledTimes(1); }); it("should not cancel multiple local queries", async () => { @@ -565,15 +602,15 @@ describe("query-history", () => { const completed = localQueryHistory[0]; const failed = localQueryHistory[2]; - const cancelSpy = sandbox.spy(completed, "cancel"); - const cancelSpy2 = sandbox.spy(failed, "cancel"); + const cancelSpy = jest.spyOn(completed, "cancel"); + const cancelSpy2 = jest.spyOn(failed, "cancel"); await queryHistoryManager.handleCancel(completed, [ completed, failed, ]); - expect(cancelSpy).to.not.have.been.calledOnce; - expect(cancelSpy2).to.not.have.been.calledOnce; + expect(cancelSpy).not.toBeCalledTimes(1); + expect(cancelSpy2).not.toBeCalledTimes(1); }); it("should not cancel a single remote query", async () => { @@ -583,7 +620,7 @@ describe("query-history", () => { const completed = remoteQueryHistory[0]; await queryHistoryManager.handleCancel(completed, [completed]); - expect(mockCancelRemoteQuery).to.not.have.been.calledWith( + expect(mockCancelRemoteQuery).not.toBeCalledWith( mockCredentials, completed.remoteQuery, ); @@ -600,11 +637,11 @@ describe("query-history", () => { completed, failed, ]); - expect(mockCancelRemoteQuery).to.not.have.been.calledWith( + expect(mockCancelRemoteQuery).not.toBeCalledWith( mockCredentials, completed.remoteQuery, ); - expect(mockCancelRemoteQuery).to.not.have.been.calledWith( + expect(mockCancelRemoteQuery).not.toBeCalledWith( mockCredentials, failed.remoteQuery, ); @@ -619,7 +656,7 @@ describe("query-history", () => { await queryHistoryManager.handleCancel(completedVariantAnalysis, [ completedVariantAnalysis, ]); - expect(executeCommandSpy).to.not.have.been.calledWith( + expect(executeCommandSpy).not.toBeCalledWith( "codeQL.cancelVariantAnalysis", completedVariantAnalysis.variantAnalysis, ); @@ -636,11 +673,11 @@ describe("query-history", () => { completedVariantAnalysis, failedVariantAnalysis, ]); - expect(executeCommandSpy).to.not.have.been.calledWith( + expect(executeCommandSpy).not.toBeCalledWith( "codeQL.cancelVariantAnalysis", completedVariantAnalysis.variantAnalysis.id, ); - expect(executeCommandSpy).to.not.have.been.calledWith( + expect(executeCommandSpy).not.toBeCalledWith( "codeQL.cancelVariantAnalysis", failedVariantAnalysis.variantAnalysis.id, ); @@ -655,7 +692,7 @@ describe("query-history", () => { const item = localQueryHistory[4]; await queryHistoryManager.handleCopyRepoList(item, [item]); - expect(executeCommandSpy).to.not.have.been.called; + expect(executeCommandSpy).not.toBeCalled(); }); it("should copy repo list for a single remote query", async () => { @@ -663,7 +700,7 @@ describe("query-history", () => { const item = remoteQueryHistory[1]; await queryHistoryManager.handleCopyRepoList(item, [item]); - expect(executeCommandSpy).to.have.been.calledWith( + expect(executeCommandSpy).toBeCalledWith( "codeQL.copyRepoList", item.queryId, ); @@ -675,7 +712,7 @@ describe("query-history", () => { const item1 = remoteQueryHistory[1]; const item2 = remoteQueryHistory[3]; await queryHistoryManager.handleCopyRepoList(item1, [item1, item2]); - expect(executeCommandSpy).not.to.have.been.called; + expect(executeCommandSpy).not.toBeCalled(); }); it("should copy repo list for a single variant analysis", async () => { @@ -683,7 +720,7 @@ describe("query-history", () => { const item = variantAnalysisHistory[1]; await queryHistoryManager.handleCopyRepoList(item, [item]); - expect(executeCommandSpy).to.have.been.calledWith( + expect(executeCommandSpy).toBeCalledWith( "codeQL.copyVariantAnalysisRepoList", item.variantAnalysis.id, ); @@ -695,7 +732,7 @@ describe("query-history", () => { const item1 = variantAnalysisHistory[1]; const item2 = variantAnalysisHistory[3]; await queryHistoryManager.handleCopyRepoList(item1, [item1, item2]); - expect(executeCommandSpy).not.to.have.been.called; + expect(executeCommandSpy).not.toBeCalled(); }); }); @@ -706,7 +743,7 @@ describe("query-history", () => { const item = localQueryHistory[4]; await queryHistoryManager.handleExportResults(item, [item]); - expect(executeCommandSpy).to.not.have.been.called; + expect(executeCommandSpy).not.toBeCalled(); }); it("should export results for a single remote query", async () => { @@ -714,7 +751,7 @@ describe("query-history", () => { const item = remoteQueryHistory[1]; await queryHistoryManager.handleExportResults(item, [item]); - expect(executeCommandSpy).to.have.been.calledWith( + expect(executeCommandSpy).toBeCalledWith( "codeQL.exportRemoteQueryResults", item.queryId, ); @@ -726,7 +763,7 @@ describe("query-history", () => { const item1 = remoteQueryHistory[1]; const item2 = remoteQueryHistory[3]; await queryHistoryManager.handleExportResults(item1, [item1, item2]); - expect(executeCommandSpy).not.to.have.been.called; + expect(executeCommandSpy).not.toBeCalled(); }); it("should export results for a single variant analysis", async () => { @@ -734,7 +771,7 @@ describe("query-history", () => { const item = variantAnalysisHistory[1]; await queryHistoryManager.handleExportResults(item, [item]); - expect(executeCommandSpy).to.have.been.calledWith( + expect(executeCommandSpy).toBeCalledWith( "codeQL.exportVariantAnalysisResults", item.variantAnalysis.id, ); @@ -746,7 +783,7 @@ describe("query-history", () => { const item1 = variantAnalysisHistory[1]; const item2 = variantAnalysisHistory[3]; await queryHistoryManager.handleExportResults(item1, [item1, item2]); - expect(executeCommandSpy).not.to.have.been.called; + expect(executeCommandSpy).not.toBeCalled(); }); }); @@ -760,7 +797,7 @@ describe("query-history", () => { singleItem, multipleItems, ); - expect(selection).to.deep.eq({ + expect(selection).toEqual({ finalSingleItem: singleItem, finalMultiSelect: multipleItems, }); @@ -772,7 +809,7 @@ describe("query-history", () => { undefined, multipleItems, ); - expect(selection).to.deep.eq({ + expect(selection).toEqual({ finalSingleItem: multipleItems[0], finalMultiSelect: multipleItems, }); @@ -784,7 +821,7 @@ describe("query-history", () => { singleItem, undefined, ); - expect(selection).to.deep.eq({ + expect(selection).toEqual({ finalSingleItem: singleItem, finalMultiSelect: [singleItem], }); @@ -801,7 +838,7 @@ describe("query-history", () => { undefined, undefined, ); - expect(selection).to.deep.eq({ + expect(selection).toEqual({ finalSingleItem: allHistory[1], finalMultiSelect: [allHistory[1]], }); @@ -830,7 +867,7 @@ describe("query-history", () => { undefined, undefined, ); - expect(selection).to.deep.eq({ + expect(selection).toEqual({ finalSingleItem: allHistory[1], finalMultiSelect: [allHistory[1]], }); @@ -842,17 +879,23 @@ describe("query-history", () => { it("should find the second query to compare when one is selected", async () => { const thisQuery = localQueryHistory[3]; queryHistoryManager = await createMockQueryHistory(allHistory); - showQuickPickSpy.returns({ query: localQueryHistory[0] }); + showQuickPickSpy.mockResolvedValue({ + query: localQueryHistory[0], + } as unknown as QuickPickItem); const otherQuery = await ( queryHistoryManager as any ).findOtherQueryToCompare(thisQuery, []); - expect(otherQuery).to.eq(localQueryHistory[0]); + expect(otherQuery).toBe(localQueryHistory[0]); // only called with first item, other items filtered out - expect(showQuickPickSpy.getCalls().length).to.eq(1); - expect(showQuickPickSpy.firstCall.args[0][0].query).to.eq( - localQueryHistory[0], + expect(showQuickPickSpy).toHaveBeenCalledTimes(1); + expect(showQuickPickSpy).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + query: localQueryHistory[0], + }), + ]), ); }); @@ -863,12 +906,16 @@ describe("query-history", () => { const otherQuery = await ( queryHistoryManager as any ).findOtherQueryToCompare(thisQuery, []); - expect(otherQuery).to.be.undefined; + expect(otherQuery).toBeUndefined(); // only called with first item, other items filtered out - expect(showQuickPickSpy.getCalls().length).to.eq(1); - expect(showQuickPickSpy.firstCall.args[0][0].query).to.eq( - localQueryHistory[0], + expect(showQuickPickSpy).toHaveBeenCalledTimes(1); + expect(showQuickPickSpy).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + query: localQueryHistory[0], + }), + ]), ); }); @@ -882,8 +929,8 @@ describe("query-history", () => { thisQuery, localQueryHistory[0], ]); - expect(otherQuery).to.eq(localQueryHistory[0]); - expect(showQuickPickSpy).not.to.have.been.called; + expect(otherQuery).toBe(localQueryHistory[0]); + expect(showQuickPickSpy).not.toBeCalled(); }); it("should throw an error when a query is not successful", async () => { @@ -892,7 +939,6 @@ describe("query-history", () => { allHistory[0] = createMockLocalQueryInfo({ dbName: "a", queryWithResults: createMockQueryWithResults({ - sandbox, didRunSuccessfully: false, }), }); @@ -902,9 +948,9 @@ describe("query-history", () => { thisQuery, [thisQuery, allHistory[0]], ); - assert(false, "Should have thrown"); + fail("Should have thrown"); } catch (e) { - expect(getErrorMessage(e)).to.eq( + expect(getErrorMessage(e)).toBe( "Please select a successful query.", ); } @@ -920,9 +966,9 @@ describe("query-history", () => { localQueryHistory[0], [localQueryHistory[0], localQueryHistory[1]], ); - assert(false, "Should have thrown"); + fail("Should have thrown"); } catch (e) { - expect(getErrorMessage(e)).to.eq( + expect(getErrorMessage(e)).toBe( "Query databases must be the same.", ); } @@ -937,9 +983,9 @@ describe("query-history", () => { thisQuery, [thisQuery, localQueryHistory[0], localQueryHistory[1]], ); - assert(false, "Should have thrown"); + fail("Should have thrown"); } catch (e) { - expect(getErrorMessage(e)).to.eq( + expect(getErrorMessage(e)).toBe( "Please select no more than 2 queries.", ); } @@ -953,7 +999,8 @@ describe("query-history", () => { localQueryHistory[0], localQueryHistory[3], ]); - expect(doCompareCallback).to.have.been.calledOnceWith( + expect(doCompareCallback).toHaveBeenCalledTimes(1); + expect(doCompareCallback).toHaveBeenCalledWith( localQueryHistory[0], localQueryHistory[3], ); @@ -964,7 +1011,7 @@ describe("query-history", () => { await queryHistoryManager.handleCompareWith(localQueryHistory[0], [ localQueryHistory[0], ]); - expect(doCompareCallback).not.to.have.been.called; + expect(doCompareCallback).not.toBeCalled(); }); }); @@ -972,14 +1019,14 @@ describe("query-history", () => { it("should update compareWithItem when there is a single item", async () => { queryHistoryManager = await createMockQueryHistory([]); (queryHistoryManager as any).updateCompareWith(["a"]); - expect(queryHistoryManager.compareWithItem).to.be.eq("a"); + expect(queryHistoryManager.compareWithItem).toBe("a"); }); it("should delete compareWithItem when there are 0 items", async () => { queryHistoryManager = await createMockQueryHistory([]); queryHistoryManager.compareWithItem = localQueryHistory[0]; (queryHistoryManager as any).updateCompareWith([]); - expect(queryHistoryManager.compareWithItem).to.be.undefined; + expect(queryHistoryManager.compareWithItem).toBeUndefined(); }); it("should delete compareWithItem when there are more than 2 items", async () => { @@ -990,7 +1037,7 @@ describe("query-history", () => { localQueryHistory[1], localQueryHistory[2], ]); - expect(queryHistoryManager.compareWithItem).to.be.undefined; + expect(queryHistoryManager.compareWithItem).toBeUndefined(); }); it("should delete compareWithItem when there are 2 items and disjoint from compareWithItem", async () => { @@ -1000,7 +1047,7 @@ describe("query-history", () => { localQueryHistory[1], localQueryHistory[2], ]); - expect(queryHistoryManager.compareWithItem).to.be.undefined; + expect(queryHistoryManager.compareWithItem).toBeUndefined(); }); it("should do nothing when compareWithItem exists and exactly 2 items", async () => { @@ -1010,7 +1057,7 @@ describe("query-history", () => { localQueryHistory[0], localQueryHistory[1], ]); - expect(queryHistoryManager.compareWithItem).to.be.eq( + expect(queryHistoryManager.compareWithItem).toBe( localQueryHistory[0], ); }); @@ -1018,7 +1065,8 @@ describe("query-history", () => { }); describe("query history scrubber", () => { - let clock: sinon.SinonFakeTimers; + const now = Date.now(); + let deregister: vscode.Disposable | undefined; let mockCtx: vscode.ExtensionContext; let runCount = 0; @@ -1031,12 +1079,14 @@ describe("query-history", () => { }); beforeEach(() => { - clock = sandbox.useFakeTimers({ - toFake: ["setInterval", "Date"], + jest.useFakeTimers({ + doNotFake: ["setTimeout"], + now, }); + mockCtx = { globalState: { - lastScrubTime: Date.now(), + lastScrubTime: now, get(key: string) { if (key !== "lastScrubTime") { throw new Error(`Unexpected key: ${key}`); @@ -1054,44 +1104,45 @@ describe("query-history", () => { }); afterEach(() => { - clock.restore(); if (deregister) { deregister.dispose(); deregister = undefined; } }); - it("should not throw an error when the query directory does not exist", async function () { + it("should not throw an error when the query directory does not exist", async () => { // because of the waits, we need to have a higher timeout on this test. - this.timeout(5000); + jest.setTimeout(5000); registerScrubber("idontexist"); - clock.tick(ONE_HOUR_IN_MS); + jest.advanceTimersByTime(ONE_HOUR_IN_MS); await wait(); - expect(runCount, "Should not have called the scrubber").to.eq(0); + // "Should not have called the scrubber" + expect(runCount).toBe(0); - clock.tick(ONE_HOUR_IN_MS - 1); + jest.advanceTimersByTime(ONE_HOUR_IN_MS - 1); await wait(); - expect(runCount, "Should not have called the scrubber").to.eq(0); + // "Should not have called the scrubber" + expect(runCount).toBe(0); - clock.tick(1); + jest.advanceTimersByTime(1); await wait(); - expect(runCount, "Should have called the scrubber once").to.eq(1); + // "Should have called the scrubber once" + expect(runCount).toBe(1); - clock.tick(TWO_HOURS_IN_MS); + jest.advanceTimersByTime(TWO_HOURS_IN_MS); await wait(); - expect(runCount, "Should have called the scrubber a second time").to.eq( - 2, - ); + // "Should have called the scrubber a second time" + expect(runCount).toBe(2); - expect((mockCtx.globalState as any).lastScrubTime).to.eq( - TWO_HOURS_IN_MS * 2, - "Should have scrubbed the last time at 4 hours.", + expect((mockCtx.globalState as any).lastScrubTime).toBe( + now + TWO_HOURS_IN_MS * 2, ); }); - it("should scrub directories", async function () { - this.timeout(5000); + it("should scrub directories", async () => { + jest.setTimeout(5000); + // create two query directories that are right around the cut off time const queryDir = createMockQueryDir( ONE_HOUR_IN_MS, @@ -1100,7 +1151,7 @@ describe("query-history", () => { ); registerScrubber(queryDir); - clock.tick(TWO_HOURS_IN_MS); + jest.advanceTimersByTime(TWO_HOURS_IN_MS); await wait(); // should have deleted only the invalid locations @@ -1111,7 +1162,7 @@ describe("query-history", () => { toQueryDirName(THREE_HOURS_IN_MS), ); - clock.tick(LESS_THAN_ONE_DAY); + jest.advanceTimersByTime(LESS_THAN_ONE_DAY); await wait(); // nothing should have happened...yet @@ -1122,7 +1173,7 @@ describe("query-history", () => { toQueryDirName(THREE_HOURS_IN_MS), ); - clock.tick(1000); + jest.advanceTimersByTime(1000); await wait(); // should have deleted the two older directories @@ -1131,7 +1182,7 @@ describe("query-history", () => { expectDirectories(queryDir, toQueryDirName(THREE_HOURS_IN_MS)); // Wait until the next scrub time and the final directory is deleted - clock.tick(TWO_HOURS_IN_MS); + jest.advanceTimersByTime(TWO_HOURS_IN_MS); await wait(); // should have deleted everything @@ -1140,7 +1191,7 @@ describe("query-history", () => { function expectDirectories(queryDir: string, ...dirNames: string[]) { const files = fs.readdirSync(queryDir); - expect(files.sort()).to.deep.eq(dirNames.sort()); + expect(files.sort()).toEqual(dirNames.sort()); } function createMockQueryDir(...timestamps: number[]) { @@ -1170,10 +1221,7 @@ describe("query-history", () => { timestamps.forEach((timestamp) => { const dir = path.join(queryDir, toQueryDirName(timestamp)); fs.mkdirSync(dir); - fs.writeFileSync( - path.join(dir, "timestamp"), - `${Date.now() + timestamp}`, - ); + fs.writeFileSync(path.join(dir, "timestamp"), `${now + timestamp}`); }); return queryDir; @@ -1224,12 +1272,11 @@ describe("query-history", () => { historyTreeDataProvider.dispose(); }); - describe("getTreeItem", async () => { + describe("getTreeItem", () => { it("should get a tree item with raw results", async () => { const mockQueryWithRawResults = createMockLocalQueryInfo({ dbName: "a", queryWithResults: createMockQueryWithResults({ - sandbox, didRunSuccessfully: true, hasInterpretedResults: false, }), @@ -1238,15 +1285,15 @@ describe("query-history", () => { const treeItem = await historyTreeDataProvider.getTreeItem( mockQueryWithRawResults, ); - expect(treeItem.command).to.deep.eq({ + expect(treeItem.command).toEqual({ title: "Query History Item", command: "codeQLQueryHistory.itemClicked", arguments: [mockQueryWithRawResults], tooltip: labelProvider.getLabel(mockQueryWithRawResults), }); - expect(treeItem.label).to.contain("query-file.ql"); - expect(treeItem.contextValue).to.eq("rawResultsItem"); - expect(treeItem.iconPath).to.deep.eq( + expect(treeItem.label).toContain("query-file.ql"); + expect(treeItem.contextValue).toBe("rawResultsItem"); + expect(treeItem.iconPath).toEqual( vscode.Uri.file(mockExtensionLocation + "/media/drive.svg").fsPath, ); }); @@ -1255,7 +1302,6 @@ describe("query-history", () => { const mockQueryWithInterpretedResults = createMockLocalQueryInfo({ dbName: "a", queryWithResults: createMockQueryWithResults({ - sandbox, didRunSuccessfully: true, hasInterpretedResults: true, }), @@ -1264,8 +1310,8 @@ describe("query-history", () => { const treeItem = await historyTreeDataProvider.getTreeItem( mockQueryWithInterpretedResults, ); - expect(treeItem.contextValue).to.eq("interpretedResultsItem"); - expect(treeItem.iconPath).to.deep.eq( + expect(treeItem.contextValue).toBe("interpretedResultsItem"); + expect(treeItem.iconPath).toEqual( vscode.Uri.file(mockExtensionLocation + "/media/drive.svg").fsPath, ); }); @@ -1275,13 +1321,12 @@ describe("query-history", () => { dbName: "a", failureReason: "failure reason", queryWithResults: createMockQueryWithResults({ - sandbox, didRunSuccessfully: false, }), }); const treeItem = await historyTreeDataProvider.getTreeItem(mockQuery); - expect(treeItem.iconPath).to.eq( + expect(treeItem.iconPath).toBe( vscode.Uri.file(mockExtensionLocation + "/media/red-x.svg").fsPath, ); }); @@ -1293,7 +1338,7 @@ describe("query-history", () => { }); const treeItem = await historyTreeDataProvider.getTreeItem(mockQuery); - expect(treeItem.iconPath).to.eq( + expect(treeItem.iconPath).toBe( vscode.Uri.file(mockExtensionLocation + "/media/red-x.svg").fsPath, ); }); @@ -1302,7 +1347,7 @@ describe("query-history", () => { const mockQuery = createMockLocalQueryInfo({ dbName: "a" }); const treeItem = await historyTreeDataProvider.getTreeItem(mockQuery); - expect(treeItem.iconPath).to.deep.eq({ + expect(treeItem.iconPath).toEqual({ id: "sync~spin", color: undefined, }); @@ -1313,8 +1358,8 @@ describe("query-history", () => { it("fetch children correctly", () => { const mockQuery = createMockLocalQueryInfo({}); historyTreeDataProvider.allHistory.push(mockQuery); - expect(historyTreeDataProvider.getChildren()).to.deep.eq([mockQuery]); - expect(historyTreeDataProvider.getChildren(mockQuery)).to.deep.eq([]); + expect(historyTreeDataProvider.getChildren()).toEqual([mockQuery]); + expect(historyTreeDataProvider.getChildren(mockQuery)).toEqual([]); }); describe("sorting", () => { @@ -1382,7 +1427,7 @@ describe("query-history", () => { treeDataProvider.sortOrder = SortOrder.NameAsc; const children = await treeDataProvider.getChildren(); - expect(children).to.deep.eq(expected); + expect(children).toEqual(expected); }); it("should get children for name descending", async () => { @@ -1390,7 +1435,7 @@ describe("query-history", () => { treeDataProvider.sortOrder = SortOrder.NameDesc; const children = await treeDataProvider.getChildren(); - expect(children).to.deep.eq(expected); + expect(children).toEqual(expected); }); it("should get children for date ascending", async () => { @@ -1407,7 +1452,7 @@ describe("query-history", () => { treeDataProvider.sortOrder = SortOrder.DateAsc; const children = await treeDataProvider.getChildren(); - expect(children).to.deep.eq(expected); + expect(children).toEqual(expected); }); it("should get children for date descending", async () => { @@ -1425,7 +1470,7 @@ describe("query-history", () => { treeDataProvider.sortOrder = SortOrder.DateDesc; const children = await treeDataProvider.getChildren(); - expect(children).to.deep.eq(expected); + expect(children).toEqual(expected); }); it("should get children for result count ascending", async () => { @@ -1443,7 +1488,7 @@ describe("query-history", () => { const children = await treeDataProvider.getChildren(); - expect(children).to.deep.eq(expected); + expect(children).toEqual(expected); }); it("should get children for result count descending", async () => { @@ -1460,7 +1505,7 @@ describe("query-history", () => { treeDataProvider.sortOrder = SortOrder.CountDesc; const children = await treeDataProvider.getChildren(); - expect(children).to.deep.eq(expected); + expect(children).toEqual(expected); }); it("should fall back to name ascending when there are no results", async () => { @@ -1503,7 +1548,7 @@ describe("query-history", () => { const children = await treeDataProvider.getChildren(); - expect(children).to.deep.eq(expected); + expect(children).toEqual(expected); }); it("should fall back to name descending when there are no results", async () => { @@ -1544,7 +1589,7 @@ describe("query-history", () => { treeDataProvider.sortOrder = SortOrder.CountDesc; const children = await treeDataProvider.getChildren(); - expect(children).to.deep.eq(expected); + expect(children).toEqual(expected); }); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts index a4905c010..99f31fe69 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts @@ -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, diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/export-results.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/export-results.test.ts index 39aa062c2..bbe75c3db 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/export-results.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/export-results.test.ts @@ -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>; - 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(), ); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts index ece65b777..fcd71dc3b 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts @@ -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>; + 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", diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-queries-api.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-queries-api.test.ts index 7048f6010..3a98b6bc3 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-queries-api.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-queries-api.test.ts @@ -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.", "", diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts index 41d4453ac..fb6736535 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts @@ -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); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts index 3a98e3120..131b53cbe 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts @@ -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"]); }); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts index 1d635003e..eea3511b3 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts @@ -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, ); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts index fa323ee15..289f64216 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts @@ -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 = {}; 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); }); } diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/sarifParser.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/sarifParser.test.ts index c4d5f7ce9..8fd075f32 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/sarifParser.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/sarifParser.test.ts @@ -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); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts index 7f683e510..525166047 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts @@ -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) { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/test-adapter.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/test-adapter.test.ts index bccd11c9e..bfdc313c0 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/test-adapter.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/test-adapter.test.ts @@ -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, From 3da00349538fbe2828828e8b3e1467785bd34279 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 22 Nov 2022 16:17:21 +0100 Subject: [PATCH 04/58] Do not run integration tests in parallel This results in parallel downloads of VSCode, which is not supported. --- extensions/ql-vscode/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 5e372b3d6..63477d4e6 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1272,7 +1272,7 @@ "test": "npm-run-all -p test:*", "test:unit": "jest --projects test", "test:view": "jest --projects src/view", - "integration": "npm-run-all -p integration:*", + "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", "cli-integration": "node ./out/vscode-tests/run-integration-tests.js cli-integration", From 65bb61b530097d3025e36b22d1ae466ddcc2c726 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 22 Nov 2022 17:14:55 +0100 Subject: [PATCH 05/58] Set timeouts on tests correctly The timeouts need to be set either on a per-file basis, or per test by using the parameter in `it`. Since we have both Mocha and Jest types, we need to declare in the test file which one we're using. --- .../no-workspace/query-history.test.ts | 4 - .../no-workspace/query-results.test.ts | 394 +++++++++--------- .../remote-query-history.test.ts | 8 +- .../variant-analysis-history.test.ts | 6 +- .../no-workspace/telemetry.test.ts | 9 +- 5 files changed, 213 insertions(+), 208 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts index bf271ba1b..d5bcea0cf 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts @@ -1111,8 +1111,6 @@ describe("query-history", () => { }); it("should not throw an error when the query directory does not exist", async () => { - // because of the waits, we need to have a higher timeout on this test. - jest.setTimeout(5000); registerScrubber("idontexist"); jest.advanceTimersByTime(ONE_HOUR_IN_MS); @@ -1141,8 +1139,6 @@ describe("query-history", () => { }); it("should scrub directories", async () => { - jest.setTimeout(5000); - // create two query directories that are right around the cut off time const queryDir = createMockQueryDir( ONE_HOUR_IN_MS, diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts index 99f31fe69..742c37523 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts @@ -26,6 +26,9 @@ import { import { EvaluationResult, QueryResultType } from "../../pure/legacy-messages"; import { sleep } from "../../pure/time"; +// Temporary until Mocha is fully removed. This is necessary for passing timeouts to `it`. +declare let it: jest.It; + describe("query-results", () => { let queryPath: string; let cnt = 0; @@ -198,93 +201,10 @@ describe("query-results", () => { safeDel(interpretedResultsPath); }); - it("should interpretResultsSarif", async () => { - // up to 2 minutes per test - jest.setTimeout(2 * 60 * 1000); - - const results = await interpretResultsSarif( - mockServer, - metadata, - { - resultsPath, - interpretedResultsPath, - }, - sourceInfo as SourceInfo, - ); - - expect(results).toEqual({ a: "1234", t: "SarifInterpretationData" }); - expect(spy).toBeCalledWith( - metadata, - resultsPath, - interpretedResultsPath, - sourceInfo, - ); - }); - - it("should interpretBqrsSarif without ID", async () => { - // up to 2 minutes per test - jest.setTimeout(2 * 60 * 1000); - - delete metadata.id; - const results = await interpretResultsSarif( - mockServer, - metadata, - { - resultsPath, - interpretedResultsPath, - }, - sourceInfo as SourceInfo, - ); - expect(results).toEqual({ a: "1234", t: "SarifInterpretationData" }); - expect(spy).toBeCalledWith( - { kind: "my-kind", id: "dummy-id", scored: undefined }, - resultsPath, - interpretedResultsPath, - sourceInfo, - ); - }); - - it("should use sarifParser on a valid small SARIF file", async () => { - // up to 2 minutes per test - jest.setTimeout(2 * 60 * 1000); - - fs.writeFileSync( - interpretedResultsPath, - JSON.stringify({ - runs: [{ results: [] }], // A run needs results to succeed. - }), - "utf8", - ); - const results = await interpretResultsSarif( - mockServer, - metadata, - { - resultsPath, - interpretedResultsPath, - }, - sourceInfo as SourceInfo, - ); - // We do not re-interpret if we are reading from a SARIF file. - expect(spy).not.toBeCalled(); - - expect(results).toHaveProperty("t", "SarifInterpretationData"); - expect(results).toHaveProperty("runs[0].results"); - }); - - it("should throw an error on an invalid small SARIF file", async () => { - // up to 2 minutes per test - jest.setTimeout(2 * 60 * 1000); - - fs.writeFileSync( - interpretedResultsPath, - JSON.stringify({ - a: "6", // Invalid: no runs or results - }), - "utf8", - ); - - await expect( - interpretResultsSarif( + it( + "should interpretResultsSarif", + async () => { + const results = await interpretResultsSarif( mockServer, metadata, { @@ -292,118 +212,145 @@ describe("query-results", () => { interpretedResultsPath, }, sourceInfo as SourceInfo, - ), - ).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).not.toBeCalled(); - }); + expect(results).toEqual({ a: "1234", t: "SarifInterpretationData" }); + expect(spy).toBeCalledWith( + metadata, + resultsPath, + interpretedResultsPath, + sourceInfo, + ); + }, + 2 * 60 * 1000, // up to 2 minutes per test + ); - it("should use sarifParser on a valid large SARIF file", async () => { - // up to 2 minutes per test - jest.setTimeout(2 * 60 * 1000); + it( + "should interpretBqrsSarif without ID", + async () => { + delete metadata.id; + const results = await interpretResultsSarif( + mockServer, + metadata, + { + resultsPath, + interpretedResultsPath, + }, + sourceInfo as SourceInfo, + ); + expect(results).toEqual({ a: "1234", t: "SarifInterpretationData" }); + expect(spy).toBeCalledWith( + { kind: "my-kind", id: "dummy-id", scored: undefined }, + resultsPath, + interpretedResultsPath, + sourceInfo, + ); + }, + 2 * 60 * 1000, // up to 2 minutes per test + ); - const validSarifStream = fs.createWriteStream(interpretedResultsPath, { - flags: "w", - }); + it( + "should use sarifParser on a valid small SARIF file", + async () => { + fs.writeFileSync( + interpretedResultsPath, + JSON.stringify({ + runs: [{ results: [] }], // A run needs results to succeed. + }), + "utf8", + ); + const results = await interpretResultsSarif( + mockServer, + metadata, + { + resultsPath, + interpretedResultsPath, + }, + sourceInfo as SourceInfo, + ); + // We do not re-interpret if we are reading from a SARIF file. + expect(spy).not.toBeCalled(); - const finished = new Promise((res, rej) => { - validSarifStream.addListener("close", res); - validSarifStream.addListener("error", rej); - }); + expect(results).toHaveProperty("t", "SarifInterpretationData"); + expect(results).toHaveProperty("runs[0].results"); + }, + 2 * 60 * 1000, // up to 2 minutes per test + ); - validSarifStream.write( - JSON.stringify({ - runs: [{ results: [] }], // A run needs results to succeed. - }), - "utf8", - ); + it( + "should throw an error on an invalid small SARIF file", + async () => { + fs.writeFileSync( + interpretedResultsPath, + JSON.stringify({ + a: "6", // Invalid: no runs or results + }), + "utf8", + ); + + await expect( + interpretResultsSarif( + mockServer, + metadata, + { + resultsPath, + interpretedResultsPath, + }, + sourceInfo as SourceInfo, + ), + ).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).not.toBeCalled(); + }, + 2 * 60 * 1000, // up to 2 minutes per test + ); + + it( + "should use sarifParser on a valid large SARIF file", + async () => { + const validSarifStream = fs.createWriteStream(interpretedResultsPath, { + flags: "w", + }); + + const finished = new Promise((res, rej) => { + validSarifStream.addListener("close", res); + validSarifStream.addListener("error", rej); + }); - validSarifStream.write("[", "utf8"); - const iterations = 1_000_000; - for (let i = 0; i < iterations; i++) { validSarifStream.write( JSON.stringify({ - a: "6", + runs: [{ results: [] }], // A run needs results to succeed. }), "utf8", ); - if (i < iterations - 1) { - validSarifStream.write(","); + + validSarifStream.write("[", "utf8"); + const iterations = 1_000_000; + for (let i = 0; i < iterations; i++) { + validSarifStream.write( + JSON.stringify({ + a: "6", + }), + "utf8", + ); + if (i < iterations - 1) { + validSarifStream.write(","); + } } - } - validSarifStream.write("]", "utf8"); - validSarifStream.end(); - await finished; + validSarifStream.write("]", "utf8"); + validSarifStream.end(); + await finished; - // We need to sleep to wait for MSFT Defender to scan the file - // so that it can be read by our test. - if (os.platform() === "win32") { - await sleep(10_000); - } - - const results = await interpretResultsSarif( - mockServer, - metadata, - { - resultsPath, - interpretedResultsPath, - }, - sourceInfo as SourceInfo, - ); - // We do not re-interpret if we are reading from a SARIF file. - expect(spy).not.toBeCalled(); - - expect(results).toHaveProperty("t", "SarifInterpretationData"); - expect(results).toHaveProperty("runs[0].results"); - }); - - it("should throw an error on an invalid large SARIF file", async () => { - // up to 2 minutes per test - 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. - const interpretedResultsPath = path.join( - tmpDir.name, - "interpreted-invalid.json", - ); - const invalidSarifStream = fs.createWriteStream(interpretedResultsPath, { - flags: "w", - }); - - const finished = new Promise((res, rej) => { - invalidSarifStream.addListener("close", res); - invalidSarifStream.addListener("error", rej); - }); - - invalidSarifStream.write("[", "utf8"); - const iterations = 1_000_000; - for (let i = 0; i < iterations; i++) { - invalidSarifStream.write( - JSON.stringify({ - a: "6", - }), - "utf8", - ); - if (i < iterations - 1) { - invalidSarifStream.write(","); + // We need to sleep to wait for MSFT Defender to scan the file + // so that it can be read by our test. + if (os.platform() === "win32") { + await sleep(10_000); } - } - invalidSarifStream.write("]", "utf8"); - invalidSarifStream.end(); - await finished; - // We need to sleep to wait for MSFT Defender to scan the file - // so that it can be read by our test. - if (os.platform() === "win32") { - await sleep(10_000); - } - - await expect( - interpretResultsSarif( + const results = await interpretResultsSarif( mockServer, metadata, { @@ -411,14 +358,79 @@ describe("query-results", () => { interpretedResultsPath, }, sourceInfo as SourceInfo, - ), - ).rejects.toThrow( - "Parsing output of interpretation failed: Invalid SARIF file: expecting at least one run with result.", - ); + ); + // We do not re-interpret if we are reading from a SARIF file. + expect(spy).not.toBeCalled(); - // We do not attempt to re-interpret if we are reading from a SARIF file. - expect(spy).not.toBeCalled(); - }); + expect(results).toHaveProperty("t", "SarifInterpretationData"); + expect(results).toHaveProperty("runs[0].results"); + }, + 2 * 60 * 1000, // up to 2 minutes per test + ); + + it( + "should throw an error on an invalid large SARIF file", + async () => { + // 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. + const interpretedResultsPath = path.join( + tmpDir.name, + "interpreted-invalid.json", + ); + const invalidSarifStream = fs.createWriteStream( + interpretedResultsPath, + { + flags: "w", + }, + ); + + const finished = new Promise((res, rej) => { + invalidSarifStream.addListener("close", res); + invalidSarifStream.addListener("error", rej); + }); + + invalidSarifStream.write("[", "utf8"); + const iterations = 1_000_000; + for (let i = 0; i < iterations; i++) { + invalidSarifStream.write( + JSON.stringify({ + a: "6", + }), + "utf8", + ); + if (i < iterations - 1) { + invalidSarifStream.write(","); + } + } + invalidSarifStream.write("]", "utf8"); + invalidSarifStream.end(); + await finished; + + // We need to sleep to wait for MSFT Defender to scan the file + // so that it can be read by our test. + if (os.platform() === "win32") { + await sleep(10_000); + } + + await expect( + interpretResultsSarif( + mockServer, + metadata, + { + resultsPath, + interpretedResultsPath, + }, + sourceInfo as SourceInfo, + ), + ).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).not.toBeCalled(); + }, + 2 * 60 * 1000, // up to 2 minutes per test + ); }); describe("splat and slurp", () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts index fb6736535..a3594084a 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts @@ -28,6 +28,9 @@ import { EvalLogViewer } from "../../../eval-log-viewer"; import { QueryRunner } from "../../../queryRunner"; import { VariantAnalysisManager } from "../../../remote-queries/variant-analysis-manager"; +// set a higher timeout since recursive delete may take a while, expecially on Windows. +jest.setTimeout(120000); + /** * Tests for remote queries and how they interact with the query history manager. */ @@ -71,9 +74,6 @@ describe("Remote queries and query history manager", () => { const openTextDocumentSpy = jest.spyOn(workspace, "openTextDocument"); beforeEach(async () => { - // set a higher timeout since recursive delete below may take a while, expecially on Windows. - 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(); @@ -129,8 +129,6 @@ describe("Remote queries and query history manager", () => { }); afterEach(() => { - // set a higher timeout since recursive delete below may take a while, expecially on Windows. - jest.setTimeout(120000); deleteHistoryState(); disposables.dispose(testDisposeHandler); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts index eea3511b3..287d76d9c 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts @@ -23,6 +23,9 @@ import { EvalLogViewer } from "../../../eval-log-viewer"; import { QueryRunner } from "../../../queryRunner"; import { VariantAnalysisManager } from "../../../remote-queries/variant-analysis-manager"; +// set a higher timeout since recursive delete may take a while, expecially on Windows. +jest.setTimeout(120000); + /** * Tests for variant analyses and how they interact with the query history manager. */ @@ -68,9 +71,6 @@ describe("Variant Analyses and QueryHistoryManager", () => { const openTextDocumentSpy = jest.spyOn(workspace, "openTextDocument"); beforeEach(async () => { - // set a higher timeout since recursive delete below may take a while, expecially on Windows. - 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(); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts index 525166047..36830c0ef 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts @@ -14,11 +14,11 @@ import { fail } from "assert"; import { ENABLE_TELEMETRY } from "../../config"; import { createMockExtensionContext } from "./index"; -describe("telemetry reporting", () => { - // setting preferences can trigger lots of background activity - // so need to bump up the timeout of this test. - jest.setTimeout(10000); +// setting preferences can trigger lots of background activity +// so need to bump up the timeout of this test. +jest.setTimeout(10000); +describe("telemetry reporting", () => { let originalTelemetryExtension: boolean | undefined; let originalTelemetryGlobal: boolean | undefined; let isCanary: string; @@ -290,7 +290,6 @@ describe("telemetry reporting", () => { 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 */), ); From 0296db8b252e5f0cc28217d1604cd4590835b7e9 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 23 Nov 2022 10:11:35 +0100 Subject: [PATCH 06/58] Move jest-runner-vscode config to correct location --- .../ql-vscode/jest-runner-vscode.config.js | 18 --------------- .../src/vscode-tests/jest.config.base.ts | 2 +- .../no-workspace/jest-runner-vscode.config.ts | 22 +++++++++++++++++++ 3 files changed, 23 insertions(+), 19 deletions(-) delete mode 100644 extensions/ql-vscode/jest-runner-vscode.config.js create mode 100644 extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts diff --git a/extensions/ql-vscode/jest-runner-vscode.config.js b/extensions/ql-vscode/jest-runner-vscode.config.js deleted file mode 100644 index 735c40f6c..000000000 --- a/extensions/ql-vscode/jest-runner-vscode.config.js +++ /dev/null @@ -1,18 +0,0 @@ -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), -}; diff --git a/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts b/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts index 6a4c0d964..7f60ffee1 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts +++ b/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts @@ -84,7 +84,7 @@ const config: Config = { 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: [], + modulePathIgnorePatterns: [".vscode-test/"], // Activates notifications for test results // notify: false, diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts new file mode 100644 index 000000000..1b9b3e2fa --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts @@ -0,0 +1,22 @@ +import * as path from "path"; +import * as tmp from "tmp-promise"; +import { RunnerOptions } from "jest-runner-vscode"; + +const tmpDir = tmp.dirSync({ unsafeCleanup: true }); + +const config: RunnerOptions = { + 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), +}; + +// 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; From 90dd97fbc021fcb0fac3369d30bf7a697d21442f Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 23 Nov 2022 10:26:39 +0100 Subject: [PATCH 07/58] Use correct jest-runner-vscode for no-workspace --- .../jest-runner-vscode.config.base.ts | 20 +++++++++++++++++++ .../no-workspace/jest-runner-vscode.config.ts | 15 ++------------ 2 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts diff --git a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts new file mode 100644 index 000000000..cf323bca4 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts @@ -0,0 +1,20 @@ +import * as path from "path"; +import * as tmp from "tmp-promise"; +import { RunnerOptions } from "jest-runner-vscode"; + +export const tmpDir = tmp.dirSync({ unsafeCleanup: true }); + +export const rootDir = path.resolve(__dirname, "../.."); + +const config: RunnerOptions = { + version: "stable", + launchArgs: [ + "--disable-extensions", + "--disable-gpu", + "--disable-workspace-trust", + "--user-data-dir=" + path.join(tmpDir.name, "user-data"), + ], + extensionDevelopmentPath: rootDir, +}; + +export default config; diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts index 1b9b3e2fa..066444e18 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts @@ -1,20 +1,9 @@ -import * as path from "path"; -import * as tmp from "tmp-promise"; import { RunnerOptions } from "jest-runner-vscode"; -const tmpDir = tmp.dirSync({ unsafeCleanup: true }); +import baseConfig from "../jest-runner-vscode.config.base"; const config: RunnerOptions = { - 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), + ...baseConfig, }; // We are purposefully not using export default here since that would result in an ESModule, which doesn't seem to be From a985bcb8723fc69d02d5ce859fa17d7ac57fb339 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 23 Nov 2022 11:21:31 +0100 Subject: [PATCH 08/58] Use different method of running tests --- extensions/ql-vscode/jest.config.js | 1 + extensions/ql-vscode/package.json | 2 +- .../src/vscode-tests/jest-runner-vscode.config.base.ts | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/jest.config.js b/extensions/ql-vscode/jest.config.js index 9c35edfdd..0787bf46f 100644 --- a/extensions/ql-vscode/jest.config.js +++ b/extensions/ql-vscode/jest.config.js @@ -3,6 +3,7 @@ * https://jestjs.io/docs/configuration */ +/** @type {import('@jest/types').Config.InitialOptions} */ module.exports = { projects: [ "/src/view", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 63477d4e6..dd9c87d48 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1273,7 +1273,7 @@ "test:unit": "jest --projects test", "test:view": "jest --projects src/view", "integration": "npm-run-all integration:*", - "integration:no-workspace": "jest --projects out/vscode-tests/no-workspace", + "integration:no-workspace": "jest --config=out/vscode-tests/no-workspace/jest.config.js 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", diff --git a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts index cf323bca4..9e5e1f778 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts +++ b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts @@ -11,7 +11,6 @@ const config: RunnerOptions = { launchArgs: [ "--disable-extensions", "--disable-gpu", - "--disable-workspace-trust", "--user-data-dir=" + path.join(tmpDir.name, "user-data"), ], extensionDevelopmentPath: rootDir, From 043a7b103946e43efb98f0e71bdeb197a89eb4a7 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 09:55:48 +0100 Subject: [PATCH 09/58] Use `resetMocks` option to reset mocks before every test --- .../src/vscode-tests/jest.config.base.ts | 2 +- .../ql-vscode/src/vscode-tests/jest.setup.ts | 4 ---- .../contextual/queryResolver.test.ts | 14 ++++++------- .../no-workspace/databaseFetcher.test.ts | 7 ++----- .../no-workspace/distribution.test.ts | 10 +++++----- .../vscode-tests/no-workspace/helpers.test.ts | 2 +- .../no-workspace/query-history.test.ts | 20 ++++++++----------- .../no-workspace/query-results.test.ts | 2 +- .../remote-queries/export-results.test.ts | 6 ++++-- .../gh-api/gh-actions-api-client.test.ts | 4 ---- .../remote-query-history.test.ts | 4 ---- .../repository-selection.test.ts | 14 ++++++------- .../variant-analysis-history.test.ts | 4 ---- .../no-workspace/run-queries.test.ts | 2 +- .../no-workspace/telemetry.test.ts | 8 ++++---- .../no-workspace/test-adapter.test.ts | 14 ++++++------- 16 files changed, 48 insertions(+), 69 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts b/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts index 7f60ffee1..71f9943a7 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts +++ b/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts @@ -102,7 +102,7 @@ const config: Config = { // reporters: undefined, // Automatically reset mock state before every test - // resetMocks: false, + resetMocks: true, // Reset the module registry before running each individual test // resetModules: false, diff --git a/extensions/ql-vscode/src/vscode-tests/jest.setup.ts b/extensions/ql-vscode/src/vscode-tests/jest.setup.ts index fb764fb45..a35117880 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest.setup.ts +++ b/extensions/ql-vscode/src/vscode-tests/jest.setup.ts @@ -3,7 +3,3 @@ import { env } from "vscode"; (env as any).openExternal = () => { /**/ }; - -afterAll(() => { - jest.restoreAllMocks(); -}); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts index bdb1710aa..2b15c5ea8 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts @@ -28,18 +28,18 @@ describe("queryResolver", () => { }; beforeEach(() => { - writeFileSpy.mockReset().mockImplementation(() => Promise.resolve()); + writeFileSpy.mockImplementation(() => Promise.resolve()); - getQlPackForDbschemeSpy.mockReset().mockResolvedValue({ + getQlPackForDbschemeSpy.mockResolvedValue({ dbschemePack: "dbschemePack", dbschemePackIsLibraryPack: false, }); - getPrimaryDbschemeSpy.mockReset().mockResolvedValue("primaryDbscheme"); + getPrimaryDbschemeSpy.mockResolvedValue("primaryDbscheme"); - mockCli.resolveQueriesInSuite.mockReset(); - mockCli.cliConstraints.supportsAllowLibraryPacksInResolveQueries - .mockReset() - .mockReturnValue(true); + mockCli.resolveQueriesInSuite; + mockCli.cliConstraints.supportsAllowLibraryPacksInResolveQueries.mockReturnValue( + true, + ); }); describe("resolveQueries", () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts index a45f64263..8929a24d9 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts @@ -25,9 +25,7 @@ describe("databaseFetcher", () => { } as unknown as Octokit.Octokit; beforeEach(() => { - quickPickSpy.mockReset().mockResolvedValue(undefined); - progressSpy.mockReset(); - mockRequest.mockReset(); + quickPickSpy.mockResolvedValue(undefined); }); it("should convert a GitHub nwo to a database url", async () => { @@ -135,8 +133,7 @@ describe("databaseFetcher", () => { const progressSpy = jest.fn(); beforeEach(() => { - quickPickSpy.mockReset().mockResolvedValue(undefined); - progressSpy.mockReset(); + quickPickSpy.mockResolvedValue(undefined); }); it("should convert a project url to a database url", async () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts index 26c7421af..4fc680f75 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts @@ -206,13 +206,13 @@ describe("Launcher path", () => { let launcherThatExists = ""; beforeEach(() => { - warnSpy.mockClear().mockResolvedValue(undefined); - errorSpy.mockClear().mockResolvedValue(undefined); - logSpy.mockClear().mockResolvedValue(undefined); - pathExistsSpy.mockClear().mockImplementation(async (path: string) => { + warnSpy.mockResolvedValue(undefined); + errorSpy.mockResolvedValue(undefined); + logSpy.mockResolvedValue(undefined); + pathExistsSpy.mockImplementation(async (path: string) => { return path.endsWith(launcherThatExists); }); - platformSpy.mockClear().mockReturnValue("win32"); + platformSpy.mockReturnValue("win32"); }); it("should not warn with proper launcher name", async () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts index 1020e41ed..7a17bbd2d 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts @@ -401,7 +401,7 @@ describe("helpers", () => { ); beforeEach(() => { - showInformationMessageSpy.mockClear().mockResolvedValue(undefined); + showInformationMessageSpy.mockResolvedValue(undefined); }); const resolveArg = diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts index d5bcea0cf..3ab738d50 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts @@ -74,16 +74,12 @@ describe("query-history", () => { let variantAnalysisHistory: VariantAnalysisHistoryItem[]; beforeEach(() => { - showTextDocumentSpy - .mockClear() - .mockResolvedValue(undefined as unknown as TextEditor); + showTextDocumentSpy.mockResolvedValue(undefined as unknown as TextEditor); - showInformationMessageSpy.mockClear().mockResolvedValue(undefined); - showQuickPickSpy.mockClear().mockResolvedValue(undefined); - executeCommandSpy.mockClear().mockResolvedValue(undefined); - logSpy.mockClear().mockResolvedValue(undefined); - - doCompareCallback.mockReset(); + showInformationMessageSpy.mockResolvedValue(undefined); + showQuickPickSpy.mockResolvedValue(undefined); + executeCommandSpy.mockResolvedValue(undefined); + logSpy.mockResolvedValue(undefined); tryOpenExternalFile = (QueryHistoryManager.prototype as any) .tryOpenExternalFile; @@ -478,10 +474,10 @@ describe("query-history", () => { request: getOctokitStub, }), } as unknown as Credentials; - credentialsSpy.mockReset().mockResolvedValue(mockCredentials); + credentialsSpy.mockResolvedValue(mockCredentials); - mockCancelRemoteQuery.mockClear().mockResolvedValue(); - getOctokitStub.mockClear(); + mockCancelRemoteQuery.mockResolvedValue(); + getOctokitStub; }); describe("if the item is in progress", () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts index 742c37523..9a578d7ac 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts @@ -190,7 +190,7 @@ describe("query-results", () => { const sourceInfo = {}; beforeEach(() => { - spy.mockReset().mockReturnValue({ a: "1234" }); + spy.mockReturnValue({ a: "1234" }); mockServer = { interpretBqrsSarif: spy, diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/export-results.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/export-results.test.ts index bbe75c3db..027ac3d71 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/export-results.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/export-results.test.ts @@ -10,8 +10,10 @@ describe("export results", () => { describe("exportRemoteQueryAnalysisResults", () => { const mockCredentials = {} as unknown as Credentials; - jest.spyOn(markdownGenerator, "generateMarkdown").mockReturnValue([]); - jest.spyOn(Credentials, "initialize").mockResolvedValue(mockCredentials); + beforeEach(() => { + 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 () { const mockCreateGist = jest diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts index fcd71dc3b..f49ac7977 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts @@ -20,10 +20,6 @@ describe("gh-actions-api-client mock responses", () => { }), } as unknown as Credentials; - beforeEach(() => { - mockRequest.mockReset(); - }); - describe("cancelRemoteQuery", () => { it("should cancel a remote query", async () => { mockRequest.mockReturnValue({ status: 202 }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts index a3594084a..377be3355 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts @@ -80,10 +80,6 @@ describe("Remote queries and query history manager", () => { disposables = new DisposableBucket(); - rehydrateRemoteQueryStub.mockReset(); - removeRemoteQueryStub.mockReset(); - openRemoteQueryResultsStub.mockReset(); - rawQueryHistory = fs.readJSONSync( path.join(STORAGE_DIR, "workspace-query-history.json"), ).queries; diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts index 131b53cbe..f7f7b6ba5 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts @@ -23,15 +23,15 @@ describe("repository selection", () => { const fsReadFileStub = jest.spyOn(fs, "readFile"); beforeEach(() => { - quickPickSpy.mockReset().mockResolvedValue(undefined); - showInputBoxSpy.mockReset().mockResolvedValue(undefined); + quickPickSpy.mockResolvedValue(undefined); + showInputBoxSpy.mockResolvedValue(undefined); - getRemoteRepositoryListsSpy.mockReset().mockReturnValue(undefined); - getRemoteRepositoryListsPathSpy.mockReset().mockReturnValue(undefined); + getRemoteRepositoryListsSpy.mockReturnValue(undefined); + getRemoteRepositoryListsPathSpy.mockReturnValue(undefined); - pathExistsStub.mockReset().mockImplementation(() => false); - fsStatStub.mockReset().mockRejectedValue(new Error("not found")); - fsReadFileStub.mockReset().mockRejectedValue(new Error("not found")); + pathExistsStub.mockImplementation(() => false); + fsStatStub.mockRejectedValue(new Error("not found")); + fsReadFileStub.mockRejectedValue(new Error("not found")); }); describe("repo lists from settings", () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts index 287d76d9c..06e19fe69 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts @@ -77,10 +77,6 @@ describe("Variant Analyses and QueryHistoryManager", () => { disposables = new DisposableBucket(); - rehydrateVariantAnalysisStub.mockReset(); - removeVariantAnalysisStub.mockReset(); - showViewStub.mockReset(); - rawQueryHistory = fs.readJSONSync( path.join(STORAGE_DIR, "workspace-query-history.json"), ).queries; diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts index 289f64216..e8f23c674 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts @@ -21,7 +21,7 @@ describe("run-queries", () => { const isCanarySpy = jest.spyOn(config, "isCanary"); beforeEach(() => { - isCanarySpy.mockReset().mockReturnValue(false); + isCanarySpy.mockReturnValue(false); }); it("should create a QueryEvaluationInfo", () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts index 36830c0ef..0f3614595 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts @@ -50,11 +50,11 @@ describe("telemetry reporting", () => { ctx = createMockExtensionContext(); - sendTelemetryEventSpy.mockReset().mockReturnValue(undefined); - sendTelemetryExceptionSpy.mockReset().mockReturnValue(undefined); - disposeSpy.mockReset().mockResolvedValue(undefined); + sendTelemetryEventSpy.mockReturnValue(undefined); + sendTelemetryExceptionSpy.mockReturnValue(undefined); + disposeSpy.mockResolvedValue(undefined); - showInformationMessageSpy.mockReset().mockResolvedValue(undefined); + showInformationMessageSpy.mockResolvedValue(undefined); originalTelemetryExtension = workspace .getConfiguration() diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/test-adapter.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/test-adapter.test.ts index bfdc313c0..4af6e9892 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/test-adapter.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/test-adapter.test.ts @@ -42,12 +42,12 @@ describe("test-adapter", () => { beforeEach(() => { mockRunTests(); - openDatabaseSpy.mockReset().mockResolvedValue(postTestDatabaseItem); - removeDatabaseItemSpy.mockReset().mockResolvedValue(undefined); - renameDatabaseItemSpy.mockReset().mockResolvedValue(undefined); - setCurrentDatabaseItemSpy.mockReset().mockResolvedValue(undefined); - resolveQlpacksSpy.mockReset().mockResolvedValue({}); - resolveTestsSpy.mockReset().mockResolvedValue([]); + openDatabaseSpy.mockResolvedValue(postTestDatabaseItem); + removeDatabaseItemSpy.mockResolvedValue(undefined); + renameDatabaseItemSpy.mockResolvedValue(undefined); + setCurrentDatabaseItemSpy.mockResolvedValue(undefined); + resolveQlpacksSpy.mockResolvedValue({}); + resolveTestsSpy.mockResolvedValue([]); fakeDatabaseManager = { openDatabase: openDatabaseSpy, removeDatabaseItem: removeDatabaseItemSpy, @@ -124,7 +124,7 @@ describe("test-adapter", () => { databaseItems = [preTestDatabaseItem]; await adapter.run(["/path/to/test/dir"]); - expect(removeDatabaseItemSpy.mock.invocationCallOrder[0]).toBeGreaterThan( + expect(removeDatabaseItemSpy.mock.invocationCallOrder[0]).toBeLessThan( runTestsSpy.mock.invocationCallOrder[0], ); expect(openDatabaseSpy.mock.invocationCallOrder[0]).toBeGreaterThan( From 347d2349061caaca9573a7a61b0aa105b32f1ef3 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 09:56:08 +0100 Subject: [PATCH 10/58] Use projects arg to run the Jest tests --- extensions/ql-vscode/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index dd9c87d48..63477d4e6 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1273,7 +1273,7 @@ "test:unit": "jest --projects test", "test:view": "jest --projects src/view", "integration": "npm-run-all integration:*", - "integration:no-workspace": "jest --config=out/vscode-tests/no-workspace/jest.config.js out/vscode-tests/no-workspace", + "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", From 01f6576e9545fb8b348c28d27c17a5ed4480604b Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 09:58:10 +0100 Subject: [PATCH 11/58] Clarify comment for activation test --- .../vscode-tests/no-workspace/activation/activation.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/activation/activation.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/activation/activation.test.ts index 8019f26de..daa3c9452 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/activation/activation.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/activation/activation.test.ts @@ -1,5 +1,7 @@ -// 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. +// This file needs to be located in a separate directory from all other tests. The jest-runner-vscode will +// create a new VSCode instance for every directory containing tests, so this will ensure that this +// test is run at the start-up of a new VSCode instance. No other files should be located in this directory since +// those may activate the extension before this test is run. import * as vscode from "vscode"; From e26e1113be8e23c2dcce377f016e6e2521163dcd Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 10:04:37 +0100 Subject: [PATCH 12/58] Remove calls to `fail` Instead of calling `fail` and catching an error, this will use the `rejects.toThrow` assertion to check that a Promise rejects with a specific error. --- .../no-workspace/query-history.test.ts | 49 +++++++------------ .../gh-api/gh-actions-api-client.test.ts | 3 +- .../remote-query-history.test.ts | 12 ++--- .../no-workspace/telemetry.test.ts | 25 +++------- 4 files changed, 30 insertions(+), 59 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts index 3ab738d50..e401d2d34 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts @@ -20,7 +20,6 @@ import { TWO_HOURS_IN_MS, } from "../../pure/time"; import { tmpDir } from "../../helpers"; -import { getErrorMessage } from "../../pure/helpers-pure"; import { HistoryItemLabelProvider } from "../../history-item-label-provider"; import { RemoteQueriesManager } from "../../remote-queries/remote-queries-manager"; import { ResultsView } from "../../interface"; @@ -939,52 +938,38 @@ describe("query-history", () => { }), }); - try { - await (queryHistoryManager as any).findOtherQueryToCompare( + await expect( + (queryHistoryManager as any).findOtherQueryToCompare(thisQuery, [ thisQuery, - [thisQuery, allHistory[0]], - ); - fail("Should have thrown"); - } catch (e) { - expect(getErrorMessage(e)).toBe( - "Please select a successful query.", - ); - } + allHistory[0], + ]), + ).rejects.toThrow("Please select a successful query."); }); it("should throw an error when a databases are not the same", async () => { queryHistoryManager = await createMockQueryHistory(allHistory); - try { - // localQueryHistory[0] is database a - // localQueryHistory[1] is database b - await (queryHistoryManager as any).findOtherQueryToCompare( + // localQueryHistory[0] is database a + // localQueryHistory[1] is database b + await expect( + (queryHistoryManager as any).findOtherQueryToCompare( localQueryHistory[0], [localQueryHistory[0], localQueryHistory[1]], - ); - fail("Should have thrown"); - } catch (e) { - expect(getErrorMessage(e)).toBe( - "Query databases must be the same.", - ); - } + ), + ).rejects.toThrow("Query databases must be the same."); }); it("should throw an error when more than 2 queries selected", async () => { const thisQuery = localQueryHistory[3]; queryHistoryManager = await createMockQueryHistory(allHistory); - try { - await (queryHistoryManager as any).findOtherQueryToCompare( + await expect( + (queryHistoryManager as any).findOtherQueryToCompare(thisQuery, [ thisQuery, - [thisQuery, localQueryHistory[0], localQueryHistory[1]], - ); - fail("Should have thrown"); - } catch (e) { - expect(getErrorMessage(e)).toBe( - "Please select no more than 2 queries.", - ); - } + localQueryHistory[0], + localQueryHistory[1], + ]), + ).rejects.toThrow("Please select no more than 2 queries."); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts index f49ac7977..22b8f78ae 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/gh-api/gh-actions-api-client.test.ts @@ -1,4 +1,3 @@ -import { fail } from "assert"; import { Credentials } from "../../../../authentication"; import { cancelRemoteQuery, @@ -124,7 +123,7 @@ describe("gh-actions-api-client real responses", () => { function skip() { if (!process.env.VSCODE_CODEQL_GITHUB_TOKEN) { if (process.env.CI) { - fail( + throw new Error( "The VSCODE_CODEQL_GITHUB_TOKEN must be set to a valid GITHUB token on CI", ); } else { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts index 377be3355..59aa559aa 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts @@ -20,7 +20,6 @@ import { RemoteQueryResult } from "../../../remote-queries/shared/remote-query-r import { DisposableBucket } from "../../disposable-bucket"; import { testDisposeHandler } from "../../test-dispose-handler"; import { walkDirectory } from "../../../helpers"; -import { getErrorMessage } from "../../../pure/helpers-pure"; import { HistoryItemLabelProvider } from "../../../history-item-label-provider"; import { RemoteQueriesManager } from "../../../remote-queries/remote-queries-manager"; import { ResultsView } from "../../../interface"; @@ -394,18 +393,15 @@ describe("Remote queries and query history manager", () => { const publisher = jest.fn(); const analysisSummaries = [...remoteQueryResult0.analysisSummaries]; - try { - await arm.loadAnalysesResults( + await expect( + arm.loadAnalysesResults( analysisSummaries, { isCancellationRequested: true, } as CancellationToken, publisher, - ); - fail("Should have thrown"); - } catch (e) { - expect(getErrorMessage(e)).toContain("cancelled"); - } + ), + ).rejects.toThrow(/cancelled/); expect(publisher).not.toBeCalled(); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts index 0f3614595..d717e1294 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts @@ -10,7 +10,6 @@ import { telemetryListener as globalTelemetryListener, } from "../../telemetry"; import { UserCancellationException } from "../../commandRunner"; -import { fail } from "assert"; import { ENABLE_TELEMETRY } from "../../config"; import { createMockExtensionContext } from "./index"; @@ -105,27 +104,19 @@ describe("telemetry reporting", () => { }); it("should initialize telemetry when global option disabled", async () => { - try { - await enableTelemetry("telemetry", false); - await telemetryListener.initialize(); - expect(telemetryListener._reporter).toBeDefined(); + await enableTelemetry("telemetry", false); + await telemetryListener.initialize(); + expect(telemetryListener._reporter).toBeDefined(); - const reporter: any = telemetryListener._reporter; - expect(reporter.userOptIn).toBe(false); // disabled - } catch (e) { - fail(e as Error); - } + const reporter: any = telemetryListener._reporter; + expect(reporter.userOptIn).toBe(false); // disabled }); it("should not initialize telemetry when extension option disabled", async () => { - try { - await enableTelemetry("codeQL.telemetry", false); - await telemetryListener.initialize(); + await enableTelemetry("codeQL.telemetry", false); + await telemetryListener.initialize(); - expect(telemetryListener._reporter).toBeUndefined(); - } catch (e) { - fail(e as Error); - } + expect(telemetryListener._reporter).toBeUndefined(); }); it("should not initialize telemetry when both options disabled", async () => { From e5967c385183a3bc14893faa9110ac40ce67581f Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 23 Nov 2022 16:37:40 +0100 Subject: [PATCH 13/58] First pass on converting cli-integration tests to Jest This is a first pass on converting the cli-integration tests to Jest. It uses a custom Jest runner (based on the jest-runner-vscode) to install required extensions. It will also necessary to move some code for installating the CLI to ensure it was possible to install the CLI from outside VSCode. Most of the conversion has been done by `npx jest-codemods`, but all migration of Sinon has been done manually. --- extensions/ql-vscode/jest.config.js | 1 + extensions/ql-vscode/package.json | 2 +- extensions/ql-vscode/src/distribution.ts | 60 +- extensions/ql-vscode/src/pure/distribution.ts | 53 ++ .../cli-integration/databases.test.ts | 55 +- .../cli-integration/global.helper.ts | 103 +--- .../cli-integration/helpers.test.ts | 17 +- .../src/vscode-tests/cli-integration/index.ts | 13 - .../jest-runner-cli-integration.ts | 74 +++ .../jest-runner-vscode.config.ts | 30 + .../cli-integration/jest.config.ts | 11 + .../cli-integration/jest.setup.ts | 101 ++++ .../cli-integration/legacy-query.test.ts | 34 +- .../cli-integration/new-query.test.ts | 36 +- .../cli-integration/packaging.test.ts | 122 ++-- .../cli-integration/queries.test.ts | 46 +- .../remote-queries-manager.test.ts | 184 +++--- .../variant-analysis-manager.test.ts | 522 +++++++++--------- .../variant-analysis-monitor.test.ts | 132 ++--- .../variant-analysis-results-manager.test.ts | 58 +- ...nt-analysis-submission-integration.test.ts | 99 ++-- .../cli-integration/run-cli.test.ts | 89 +-- .../cli-integration/sourcemap.test.ts | 15 +- extensions/ql-vscode/src/vscode-tests/cli.ts | 44 ++ .../ql-vscode/src/vscode-tests/ensureCli.ts | 29 +- .../jest-runner-vscode.config.base.ts | 2 +- .../ql-vscode/src/vscode-tests/jest.setup.ts | 12 + .../no-workspace/jest-runner-vscode.config.ts | 1 + .../ql-vscode/src/vscode-tests/test-config.ts | 20 + 29 files changed, 1048 insertions(+), 917 deletions(-) create mode 100644 extensions/ql-vscode/src/pure/distribution.ts delete mode 100644 extensions/ql-vscode/src/vscode-tests/cli-integration/index.ts create mode 100644 extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-cli-integration.ts create mode 100644 extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.ts create mode 100644 extensions/ql-vscode/src/vscode-tests/cli-integration/jest.config.ts create mode 100644 extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts create mode 100644 extensions/ql-vscode/src/vscode-tests/cli.ts diff --git a/extensions/ql-vscode/jest.config.js b/extensions/ql-vscode/jest.config.js index 0787bf46f..a0e61facf 100644 --- a/extensions/ql-vscode/jest.config.js +++ b/extensions/ql-vscode/jest.config.js @@ -8,6 +8,7 @@ module.exports = { projects: [ "/src/view", "/test", + "/out/vscode-tests/cli-integration", "/out/vscode-tests/no-workspace", ], }; diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 63477d4e6..84183eecd 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1275,7 +1275,7 @@ "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", - "cli-integration": "node ./out/vscode-tests/run-integration-tests.js cli-integration", + "cli-integration": "jest --config=out/vscode-tests/cli-integration/jest.config.js out/vscode-tests/cli-integration", "update-vscode": "node ./node_modules/vscode/bin/install", "format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix", "lint": "eslint . --ext .ts,.tsx --max-warnings=0", diff --git a/extensions/ql-vscode/src/distribution.ts b/extensions/ql-vscode/src/distribution.ts index e2c2578ba..46c33d19f 100644 --- a/extensions/ql-vscode/src/distribution.ts +++ b/extensions/ql-vscode/src/distribution.ts @@ -3,7 +3,6 @@ import * as fs from "fs-extra"; import * as os from "os"; import * as path from "path"; import * as semver from "semver"; -import * as unzipper from "unzipper"; import * as url from "url"; import { ExtensionContext, Event } from "vscode"; import { DistributionConfig } from "./config"; @@ -16,6 +15,12 @@ import { import { logger } from "./logging"; import { getCodeQlCliVersion } from "./cli-version"; import { ProgressCallback, reportStreamProgress } from "./commandRunner"; +import { + codeQlLauncherName, + deprecatedCodeQlLauncherName, + extractZipArchive, + getRequiredAssetName, +} from "./pure/distribution"; /** * distribution.ts @@ -55,22 +60,6 @@ export interface DistributionProvider { } export class DistributionManager implements DistributionProvider { - /** - * Get the name of the codeql cli installation we prefer to install, based on our current platform. - */ - public static getRequiredAssetName(): string { - switch (os.platform()) { - case "linux": - return "codeql-linux64.zip"; - case "darwin": - return "codeql-osx64.zip"; - case "win32": - return "codeql-win64.zip"; - default: - return "codeql.zip"; - } - } - constructor( public readonly config: DistributionConfig, private readonly versionRange: semver.Range, @@ -361,7 +350,7 @@ class ExtensionSpecificDistributionManager { } // Filter assets to the unique one that we require. - const requiredAssetName = DistributionManager.getRequiredAssetName(); + const requiredAssetName = getRequiredAssetName(); const assets = release.assets.filter( (asset) => asset.name === requiredAssetName, ); @@ -431,7 +420,7 @@ class ExtensionSpecificDistributionManager { } private async getLatestRelease(): Promise { - const requiredAssetName = DistributionManager.getRequiredAssetName(); + const requiredAssetName = getRequiredAssetName(); void logger.log( `Searching for latest release including ${requiredAssetName}.`, ); @@ -683,39 +672,6 @@ export class ReleasesApiConsumer { private static readonly _maxRedirects = 20; } -export async function extractZipArchive( - archivePath: string, - outPath: string, -): Promise { - const archive = await unzipper.Open.file(archivePath); - await archive.extract({ - concurrency: 4, - path: outPath, - }); - // Set file permissions for extracted files - await Promise.all( - archive.files.map(async (file) => { - // Only change file permissions if within outPath (path.join normalises the path) - const extractedPath = path.join(outPath, file.path); - if ( - extractedPath.indexOf(outPath) !== 0 || - !(await fs.pathExists(extractedPath)) - ) { - return Promise.resolve(); - } - return fs.chmod(extractedPath, file.externalFileAttributes >>> 16); - }), - ); -} - -export function codeQlLauncherName(): string { - return os.platform() === "win32" ? "codeql.exe" : "codeql"; -} - -function deprecatedCodeQlLauncherName(): string | undefined { - return os.platform() === "win32" ? "codeql.cmd" : undefined; -} - function isRedirectStatusCode(statusCode: number): boolean { return ( statusCode === 301 || diff --git a/extensions/ql-vscode/src/pure/distribution.ts b/extensions/ql-vscode/src/pure/distribution.ts new file mode 100644 index 000000000..d9887b9c6 --- /dev/null +++ b/extensions/ql-vscode/src/pure/distribution.ts @@ -0,0 +1,53 @@ +import * as os from "os"; +import * as unzipper from "unzipper"; +import * as path from "path"; +import * as fs from "fs-extra"; + +/** + * Get the name of the codeql cli installation we prefer to install, based on our current platform. + */ +export function getRequiredAssetName(): string { + switch (os.platform()) { + case "linux": + return "codeql-linux64.zip"; + case "darwin": + return "codeql-osx64.zip"; + case "win32": + return "codeql-win64.zip"; + default: + return "codeql.zip"; + } +} + +export async function extractZipArchive( + archivePath: string, + outPath: string, +): Promise { + const archive = await unzipper.Open.file(archivePath); + await archive.extract({ + concurrency: 4, + path: outPath, + }); + // Set file permissions for extracted files + await Promise.all( + archive.files.map(async (file) => { + // Only change file permissions if within outPath (path.join normalises the path) + const extractedPath = path.join(outPath, file.path); + if ( + extractedPath.indexOf(outPath) !== 0 || + !(await fs.pathExists(extractedPath)) + ) { + return Promise.resolve(); + } + return fs.chmod(extractedPath, file.externalFileAttributes >>> 16); + }), + ); +} + +export function codeQlLauncherName(): string { + return os.platform() === "win32" ? "codeql.exe" : "codeql"; +} + +export function deprecatedCodeQlLauncherName(): string | undefined { + return os.platform() === "win32" ? "codeql.cmd" : undefined; +} diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts index eab03db03..4e13a524a 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts @@ -1,7 +1,4 @@ -import * as sinon from "sinon"; import * as path from "path"; -import { fail } from "assert"; -import { expect } from "chai"; import { extensions, CancellationToken, Uri, window } from "vscode"; import { CodeQLExtensionInterface } from "../../extension"; @@ -12,33 +9,29 @@ import { importArchiveDatabase, promptImportInternetDatabase, } from "../../databaseFetcher"; -import { ProgressCallback } from "../../commandRunner"; import { cleanDatabases, dbLoc, DB_URL, storagePath } from "./global.helper"; +jest.setTimeout(60_000); + /** * Run various integration tests for databases */ -describe("Databases", function () { - this.timeout(60000); - +describe("Databases", () => { const LGTM_URL = "https://lgtm.com/projects/g/aeisenberg/angular-bind-notifier/"; let databaseManager: DatabaseManager; - let sandbox: sinon.SinonSandbox; - let inputBoxStub: sinon.SinonStub; + const inputBoxStub = jest.spyOn(window, "showInputBox"); let cli: CodeQLCliServer; - let progressCallback: ProgressCallback; + const progressCallback = jest.fn(); + + jest.spyOn(window, "showErrorMessage").mockResolvedValue(undefined); + jest.spyOn(window, "showInformationMessage").mockResolvedValue(undefined); beforeEach(async () => { try { - sandbox = sinon.createSandbox(); - // the uri.fsPath function on windows returns a lowercase drive letter - // so, force the storage path string to be lowercase, too. - progressCallback = sandbox.spy(); - inputBoxStub = sandbox.stub(window, "showInputBox"); - sandbox.stub(window, "showErrorMessage"); - sandbox.stub(window, "showInformationMessage"); + inputBoxStub.mockReset().mockResolvedValue(undefined); + progressCallback.mockReset(); const extension = await extensions .getExtension>( @@ -61,7 +54,6 @@ describe("Databases", function () { afterEach(async () => { try { - sandbox.restore(); await cleanDatabases(databaseManager); } catch (e) { fail(e as Error); @@ -69,7 +61,6 @@ describe("Databases", function () { }); it("should add a database from a folder", async () => { - const progressCallback = sandbox.spy() as ProgressCallback; const uri = Uri.file(dbLoc); let dbItem = await importArchiveDatabase( uri.toString(true), @@ -79,16 +70,16 @@ describe("Databases", function () { {} as CancellationToken, cli, ); - expect(dbItem).to.be.eq(databaseManager.currentDatabaseItem); - expect(dbItem).to.be.eq(databaseManager.databaseItems[0]); - expect(dbItem).not.to.be.undefined; + expect(dbItem).toBe(databaseManager.currentDatabaseItem); + expect(dbItem).toBe(databaseManager.databaseItems[0]); + expect(dbItem).toBeDefined(); dbItem = dbItem!; - expect(dbItem.name).to.eq("db"); - expect(dbItem.databaseUri.fsPath).to.eq(path.join(storagePath, "db", "db")); + expect(dbItem.name).toBe("db"); + expect(dbItem.databaseUri.fsPath).toBe(path.join(storagePath, "db", "db")); }); it("should add a database from lgtm with only one language", async () => { - inputBoxStub.resolves(LGTM_URL); + inputBoxStub.mockResolvedValue(LGTM_URL); let dbItem = await promptImportLgtmDatabase( databaseManager, storagePath, @@ -96,10 +87,10 @@ describe("Databases", function () { {} as CancellationToken, cli, ); - expect(dbItem).not.to.be.undefined; + expect(dbItem).toBeDefined(); dbItem = dbItem!; - expect(dbItem.name).to.eq("aeisenberg_angular-bind-notifier_106179a"); - expect(dbItem.databaseUri.fsPath).to.eq( + expect(dbItem.name).toBe("aeisenberg_angular-bind-notifier_106179a"); + expect(dbItem.databaseUri.fsPath).toBe( path.join( storagePath, "javascript", @@ -109,7 +100,7 @@ describe("Databases", function () { }); it("should add a database from a url", async () => { - inputBoxStub.resolves(DB_URL); + inputBoxStub.mockResolvedValue(DB_URL); let dbItem = await promptImportInternetDatabase( databaseManager, @@ -118,10 +109,10 @@ describe("Databases", function () { {} as CancellationToken, cli, ); - expect(dbItem).not.to.be.undefined; + expect(dbItem).toBeDefined(); dbItem = dbItem!; - expect(dbItem.name).to.eq("db"); - expect(dbItem.databaseUri.fsPath).to.eq( + expect(dbItem.name).toBe("db"); + expect(dbItem.databaseUri.fsPath).toBe( path.join(storagePath, "simple-db", "db"), ); }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/global.helper.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/global.helper.ts index df1d04758..447b5bbd1 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/global.helper.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/global.helper.ts @@ -1,15 +1,8 @@ import * as path from "path"; -import * as tmp from "tmp"; import * as yaml from "js-yaml"; import * as fs from "fs-extra"; -import fetch from "node-fetch"; - -import { fail } from "assert"; -import { commands, extensions, workspace } from "vscode"; -import { CodeQLExtensionInterface } from "../../extension"; +import { commands } from "vscode"; import { DatabaseManager } from "../../databases"; -import { getTestSetting } from "../test-config"; -import { CUSTOM_CODEQL_PATH_SETTING } from "../../config"; import { CodeQLCliServer } from "../../cli"; import { removeWorkspaceRefs } from "../../remote-queries/run-remote-query"; @@ -26,98 +19,8 @@ export const dbLoc = path.join( ); export let storagePath: string; -export default function (mocha: Mocha) { - // create an extension storage location - let removeStorage: tmp.DirResult["removeCallback"] | undefined; - - // ensure the test database is downloaded - (mocha.options as any).globalSetup.push(async () => { - fs.mkdirpSync(path.dirname(dbLoc)); - if (!fs.existsSync(dbLoc)) { - try { - await new Promise((resolve, reject) => { - return fetch(DB_URL).then((response) => { - const dest = fs.createWriteStream(dbLoc); - response.body.pipe(dest); - - response.body.on("error", reject); - dest.on("error", reject); - dest.on("close", () => { - resolve(dbLoc); - }); - }); - }); - } catch (e) { - fail("Failed to download test database: " + e); - } - } - }); - - // Set the CLI version here before activation to ensure we don't accidentally try to download a cli - (mocha.options as any).globalSetup.push(async () => { - await getTestSetting(CUSTOM_CODEQL_PATH_SETTING)?.setInitialTestValue( - process.env.CLI_PATH, - ); - }); - - // Create the temp directory to be used as extension local storage. - (mocha.options as any).globalSetup.push(() => { - const dir = tmp.dirSync(); - storagePath = fs.realpathSync(dir.name); - if (storagePath.substring(0, 2).match(/[A-Z]:/)) { - storagePath = - storagePath.substring(0, 1).toLocaleLowerCase() + - storagePath.substring(1); - } - - removeStorage = dir.removeCallback; - }); - - // ensure extension is cleaned up. - (mocha.options as any).globalTeardown.push(async () => { - const extension = await extensions - .getExtension>( - "GitHub.vscode-codeql", - )! - .activate(); - // This shuts down the extension and can only be run after all tests have completed. - // If this is not called, then the test process will hang. - if ("dispose" in extension) { - try { - extension.dispose(); - } catch (e) { - console.warn("Failed to dispose extension", e); - } - } - }); - - // ensure temp directory is cleaned up. - (mocha.options as any).globalTeardown.push(() => { - try { - removeStorage?.(); - } catch (e) { - // we are exiting anyway so don't worry about it. - // most likely the directory this is a test on Windows and some files are locked. - console.warn(`Failed to remove storage directory '${storagePath}': ${e}`); - } - }); - - // check that the codeql folder is found in the workspace - (mocha.options as any).globalSetup.push(async () => { - const folders = workspace.workspaceFolders; - if (!folders) { - fail( - '\n\n\nNo workspace folders found.\nYou will need a local copy of the codeql repo.\nMake sure you specify the path to it in launch.json.\nIt should be something along the lines of "${workspaceRoot}/../codeql" depending on where you have your local copy of the codeql repo.\n\n\n', - ); - } else { - const codeqlFolder = folders.find((folder) => folder.name === "codeql"); - if (!codeqlFolder) { - fail( - '\n\n\nNo workspace folders found.\nYou will need a local copy of the codeql repo.\nMake sure you specify the path to it in launch.json.\nIt should be something along the lines of "${workspaceRoot}/../codeql" depending on where you have your local copy of the codeql repo.\n\n\n', - ); - } - } - }); +export function setStoragePath(path: string) { + storagePath = path; } export async function cleanDatabases(databaseManager: DatabaseManager) { diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/helpers.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/helpers.test.ts index ea00fc51f..2bfdf41a6 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/helpers.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/helpers.test.ts @@ -4,17 +4,16 @@ import { extensions } from "vscode"; import { CodeQLCliServer } from "../../cli"; import { CodeQLExtensionInterface } from "../../extension"; import { tryGetQueryMetadata } from "../../helpers"; -import { expect } from "chai"; -describe("helpers (with CLI)", function () { +// up to 3 minutes per test +jest.setTimeout(3 * 60 * 1000); + +describe("helpers (with CLI)", () => { const baseDir = path.join( __dirname, "../../../src/vscode-tests/cli-integration", ); - // up to 3 minutes per test - this.timeout(3 * 60 * 1000); - let cli: CodeQLCliServer; beforeEach(async () => { @@ -39,9 +38,9 @@ describe("helpers (with CLI)", function () { path.join(baseDir, "data", "simple-javascript-query.ql"), ); - expect(metadata!.name).to.equal("This is the name"); - expect(metadata!.kind).to.equal("problem"); - expect(metadata!.id).to.equal("javascript/example/test-query"); + expect(metadata!.name).toBe("This is the name"); + expect(metadata!.kind).toBe("problem"); + expect(metadata!.id).toBe("javascript/example/test-query"); }); it("should handle query with no metadata", async () => { @@ -51,6 +50,6 @@ describe("helpers (with CLI)", function () { path.join(baseDir, "data", "simple-query.ql"), ); - expect(noMetadata).to.deep.equal({}); + expect(noMetadata).toEqual({}); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/index.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/index.ts deleted file mode 100644 index 12aa174dd..000000000 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import "source-map-support/register"; -import { runTestsInDirectory } from "../index-template"; -import "mocha"; -import * as sinonChai from "sinon-chai"; -import * as chai from "chai"; -import "chai/register-should"; -import * as chaiAsPromised from "chai-as-promised"; -chai.use(chaiAsPromised); -chai.use(sinonChai); - -export function run(): Promise { - return runTestsInDirectory(__dirname, true); -} diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-cli-integration.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-cli-integration.ts new file mode 100644 index 000000000..1565ab7d1 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-cli-integration.ts @@ -0,0 +1,74 @@ +import * as cp from "child_process"; +import * as path from "path"; + +import type * as JestRunner from "jest-runner"; +import VSCodeTestRunner, { RunnerOptions } from "jest-runner-vscode"; +import { cosmiconfig } from "cosmiconfig"; +import { + downloadAndUnzipVSCode, + resolveCliArgsFromVSCodeExecutablePath, +} from "@vscode/test-electron"; +import { ensureCli } from "../ensureCli"; + +export default class JestRunnerCliIntegration extends VSCodeTestRunner { + async runTests( + tests: JestRunner.Test[], + watcher: JestRunner.TestWatcher, + onStart: JestRunner.OnTestStart, + onResult: JestRunner.OnTestSuccess, + onFailure: JestRunner.OnTestFailure, + ): Promise { + // The CLI integration tests require certain extensions to be installed, which needs to happen before the tests are + // actually run. The below code will resolve the path to the VSCode executable, and then use that to install the + // required extensions. + + const installedOnVsCodeVersions = + new Set<`${RunnerOptions["version"]}-${RunnerOptions["platform"]}`>(); + + for (const test of tests) { + const testDir = path.dirname(test.path); + + const options: RunnerOptions = + ((await cosmiconfig("jest-runner-vscode").search(testDir)) + ?.config as RunnerOptions) ?? {}; + + const { version, platform } = options; + const versionKey = `${version}-${platform}`; + + if (installedOnVsCodeVersions.has(versionKey)) { + continue; + } + + const vscodeExecutablePath = await downloadAndUnzipVSCode( + version, + platform, + ); + + console.log(`Installing required extensions for ${vscodeExecutablePath}`); + + const [cli, ...args] = + resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath); + + cp.spawnSync( + cli, + [ + ...args, + "--install-extension", + "hbenl.vscode-test-explorer", + "--install-extension", + "ms-vscode.test-adapter-converter", + ], + { + encoding: "utf-8", + stdio: "inherit", + }, + ); + + installedOnVsCodeVersions.add(versionKey); + } + + await ensureCli(true); + + return super.runTests(tests, watcher, onStart, onResult, onFailure); + } +} diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.ts new file mode 100644 index 000000000..672794e59 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.ts @@ -0,0 +1,30 @@ +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 ?? []), + // explicitly disable extensions that are known to interfere with the CLI integration tests + "--disable-extension", + "eamodio.gitlens", + "--disable-extension", + "github.codespaces", + "--disable-extension", + "github.copilot", + path.resolve(rootDir, "test/data"), + // CLI integration tests requires a multi-root workspace so that the data and the QL sources are accessible. + ...(process.env.TEST_CODEQL_PATH ? [process.env.TEST_CODEQL_PATH] : []), + ], + extensionTestsEnv: { + ...baseConfig.extensionTestsEnv, + INTEGRATION_TEST_MODE: "true", + }, +}; + +// 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; diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.config.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.config.ts new file mode 100644 index 000000000..eb7367100 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.config.ts @@ -0,0 +1,11 @@ +import type { Config } from "jest"; + +import baseConfig from "../jest.config.base"; + +const config: Config = { + ...baseConfig, + runner: "/jest-runner-cli-integration.js", + setupFilesAfterEnv: ["/jest.setup.js"], +}; + +export default config; diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts new file mode 100644 index 000000000..35ff08f27 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts @@ -0,0 +1,101 @@ +import * as fs from "fs-extra"; +import * as path from "path"; +import fetch from "node-fetch"; +import { DB_URL, dbLoc, setStoragePath, storagePath } from "./global.helper"; +import * as tmp from "tmp"; +import { getTestSetting } from "../test-config"; +import { CUSTOM_CODEQL_PATH_SETTING } from "../../config"; +import { extensions, workspace } from "vscode"; +import { CodeQLExtensionInterface } from "../../extension"; + +import baseJestSetup from "../jest.setup"; + +export default baseJestSetup; + +// create an extension storage location +let removeStorage: tmp.DirResult["removeCallback"] | undefined; + +beforeAll(async () => { + // Set the CLI version here before activation to ensure we don't accidentally try to download a cli + await getTestSetting(CUSTOM_CODEQL_PATH_SETTING)?.setInitialTestValue( + process.env.CLI_PATH, + ); + await getTestSetting(CUSTOM_CODEQL_PATH_SETTING)?.setup(); + + // ensure the test database is downloaded + fs.mkdirpSync(path.dirname(dbLoc)); + if (!fs.existsSync(dbLoc)) { + console.log(`Downloading test database to ${dbLoc}`); + + try { + await new Promise((resolve, reject) => { + return fetch(DB_URL).then((response) => { + const dest = fs.createWriteStream(dbLoc); + response.body.pipe(dest); + + response.body.on("error", reject); + dest.on("error", reject); + dest.on("close", () => { + resolve(dbLoc); + }); + }); + }); + } catch (e) { + fail("Failed to download test database: " + e); + } + } + + // Create the temp directory to be used as extension local storage. + const dir = tmp.dirSync(); + let storagePath = fs.realpathSync(dir.name); + if (storagePath.substring(0, 2).match(/[A-Z]:/)) { + storagePath = + storagePath.substring(0, 1).toLocaleLowerCase() + + storagePath.substring(1); + } + setStoragePath(storagePath); + + removeStorage = dir.removeCallback; + + // check that the codeql folder is found in the workspace + const folders = workspace.workspaceFolders; + if (!folders) { + fail( + '\n\n\nNo workspace folders found.\nYou will need a local copy of the codeql repo.\nMake sure you specify the path to it in launch.json.\nIt should be something along the lines of "${workspaceRoot}/../codeql" depending on where you have your local copy of the codeql repo.\n\n\n', + ); + } else { + const codeqlFolder = folders.find((folder) => folder.name === "codeql"); + if (!codeqlFolder) { + fail( + '\n\n\nNo workspace folders found.\nYou will need a local copy of the codeql repo.\nMake sure you specify the path to it in launch.json.\nIt should be something along the lines of "${workspaceRoot}/../codeql" depending on where you have your local copy of the codeql repo.\n\n\n', + ); + } + } +}); + +// ensure extension is cleaned up. +afterAll(async () => { + const extension = await extensions + .getExtension>( + "GitHub.vscode-codeql", + )! + .activate(); + // This shuts down the extension and can only be run after all tests have completed. + // If this is not called, then the test process will hang. + if ("dispose" in extension) { + try { + extension.dispose(); + } catch (e) { + console.warn("Failed to dispose extension", e); + } + } + + // ensure temp directory is cleaned up. + try { + removeStorage?.(); + } catch (e) { + // we are exiting anyway so don't worry about it. + // most likely the directory this is a test on Windows and some files are locked. + console.warn(`Failed to remove storage directory '${storagePath}': ${e}`); + } +}); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts index 355f713e0..6a4753040 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts @@ -1,4 +1,3 @@ -import { expect } from "chai"; import * as fs from "fs-extra"; import * as path from "path"; import * as tmp from "tmp"; @@ -11,7 +10,7 @@ import { CellValue } from "../../pure/bqrs-cli-types"; import { extensions } from "vscode"; import { CodeQLExtensionInterface } from "../../extension"; import { fail } from "assert"; -import { skipIfNoCodeQL } from "../ensureCli"; +import { describeWithCodeQL } from "../cli"; import { QueryServerClient } from "../../legacy-query-server/queryserver-client"; import { logger, ProgressReporter } from "../../logging"; @@ -100,15 +99,9 @@ const db: messages.Dataset = { workingSet: "default", }; -describe("using the legacy query server", function () { - before(function () { - skipIfNoCodeQL(this); - }); - - // Note this does not work with arrow functions as the test case bodies: - // ensure they are all written with standard anonymous functions. - this.timeout(20000); +jest.setTimeout(20_000); +describeWithCodeQL()("using the legacy query server", () => { const nullProgressReporter: ProgressReporter = { report: () => { /** ignore */ @@ -118,7 +111,7 @@ describe("using the legacy query server", function () { let qs: qsClient.QueryServerClient; let cliServer: cli.CodeQLCliServer; - before(async () => { + beforeAll(async () => { try { const extension = await extensions .getExtension>( @@ -178,8 +171,8 @@ describe("using the legacy query server", function () { } }); - it(`should be able to compile query ${queryName}`, async function () { - expect(fs.existsSync(queryTestCase.queryPath)).to.be.true; + it(`should be able to compile query ${queryName}`, async () => { + expect(fs.existsSync(queryTestCase.queryPath)).toBe(true); try { const qlProgram: messages.QlProgram = { libraryPath: [], @@ -210,14 +203,14 @@ describe("using the legacy query server", function () { /**/ }, ); - expect(result.messages!.length).to.equal(0); + expect(result.messages!.length).toBe(0); await compilationSucceeded.resolve(); } catch (e) { await compilationSucceeded.reject(e as Error); } }); - it(`should be able to run query ${queryName}`, async function () { + it(`should be able to run query ${queryName}`, async () => { try { await compilationSucceeded.done(); const callbackId = qs.registerCallback((_res) => { @@ -246,7 +239,7 @@ describe("using the legacy query server", function () { }); const actualResultSets: ResultSets = {}; - it(`should be able to parse results of query ${queryName}`, async function () { + it(`should be able to parse results of query ${queryName}`, async () => { await evaluationSucceeded.done(); const info = await cliServer.bqrsInfo(RESULTS_PATH); @@ -260,16 +253,15 @@ describe("using the legacy query server", function () { await parsedResults.resolve(); }); - it(`should have correct results for query ${queryName}`, async function () { + it(`should have correct results for query ${queryName}`, async () => { await parsedResults.done(); - expect(actualResultSets!).not.to.be.empty; - expect(Object.keys(actualResultSets!).sort()).to.eql( + expect(actualResultSets!).not.toHaveLength(0); + expect(Object.keys(actualResultSets!).sort()).toEqual( Object.keys(queryTestCase.expectedResultSets).sort(), ); for (const name in queryTestCase.expectedResultSets) { - expect(actualResultSets![name]).to.eql( + expect(actualResultSets![name]).toEqual( queryTestCase.expectedResultSets[name], - `Results for query predicate ${name} do not match`, ); } }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts index 95b3973fc..6becb807c 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts @@ -1,4 +1,3 @@ -import { expect } from "chai"; import * as path from "path"; import * as tmp from "tmp"; import { CancellationTokenSource } from "vscode-jsonrpc"; @@ -9,7 +8,7 @@ import { CellValue } from "../../pure/bqrs-cli-types"; import { extensions, Uri } from "vscode"; import { CodeQLExtensionInterface } from "../../extension"; import { fail } from "assert"; -import { skipIfNoCodeQL } from "../ensureCli"; +import { describeWithCodeQL } from "../cli"; import { QueryServerClient } from "../../query-server/queryserver-client"; import { logger, ProgressReporter } from "../../logging"; import { QueryResultType } from "../../pure/new-messages"; @@ -101,19 +100,19 @@ const nullProgressReporter: ProgressReporter = { }, }; -describe("using the new query server", function () { - before(function () { - skipIfNoCodeQL(this); - }); +jest.setTimeout(20_000); - // Note this does not work with arrow functions as the test case bodies: - // ensure they are all written with standard anonymous functions. - this.timeout(20000); +describeWithCodeQL()("using the new query server", () => { + let testContext: any; + + beforeAll(() => { + testContext = {}; + }); let qs: qsClient.QueryServerClient; let cliServer: cli.CodeQLCliServer; let db: string; - before(async () => { + beforeAll(async () => { try { const extension = await extensions .getExtension>( @@ -127,7 +126,7 @@ describe("using the new query server", function () { if ( !(await cliServer.cliConstraints.supportsNewQueryServerForTests()) ) { - this.ctx.skip(); + testContext.ctx.skip(); } qs = new QueryServerClient( { @@ -194,7 +193,7 @@ describe("using the new query server", function () { ); }); - it(`should be able to run query ${queryName}`, async function () { + it(`should be able to run query ${queryName}`, async () => { try { const params: messages.RunQueryParams = { db, @@ -213,7 +212,7 @@ describe("using the new query server", function () { /**/ }, ); - expect(result.resultType).to.equal(QueryResultType.SUCCESS); + expect(result.resultType).toBe(QueryResultType.SUCCESS); await evaluationSucceeded.resolve(); } catch (e) { await evaluationSucceeded.reject(e as Error); @@ -221,7 +220,7 @@ describe("using the new query server", function () { }); const actualResultSets: ResultSets = {}; - it(`should be able to parse results of query ${queryName}`, async function () { + it(`should be able to parse results of query ${queryName}`, async () => { await evaluationSucceeded.done(); const info = await cliServer.bqrsInfo(RESULTS_PATH); @@ -235,16 +234,15 @@ describe("using the new query server", function () { await parsedResults.resolve(); }); - it(`should have correct results for query ${queryName}`, async function () { + it(`should have correct results for query ${queryName}`, async () => { await parsedResults.done(); - expect(actualResultSets!).not.to.be.empty; - expect(Object.keys(actualResultSets!).sort()).to.eql( + expect(actualResultSets!).not.toHaveLength(0); + expect(Object.keys(actualResultSets!).sort()).toEqual( Object.keys(queryTestCase.expectedResultSets).sort(), ); for (const name in queryTestCase.expectedResultSets) { - expect(actualResultSets![name]).to.eql( + expect(actualResultSets![name]).toEqual( queryTestCase.expectedResultSets[name], - `Results for query predicate ${name} do not match`, ); } }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts index 78f7cd7cd..34150a96b 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts @@ -1,43 +1,39 @@ -import * as sinon from "sinon"; -import { extensions, window } from "vscode"; +import { extensions, QuickPickItem, window } from "vscode"; import * as path from "path"; -import * as pq from "proxyquire"; - -import { CliVersionConstraint, CodeQLCliServer } from "../../cli"; +import { CodeQLCliServer } from "../../cli"; import { CodeQLExtensionInterface } from "../../extension"; -import { expect } from "chai"; import { getErrorMessage } from "../../pure/helpers-pure"; -const proxyquire = pq.noPreserveCache(); +import * as helpers from "../../helpers"; +import { + handleDownloadPacks, + handleInstallPackDependencies, +} from "../../packaging"; -describe("Packaging commands", function () { - let sandbox: sinon.SinonSandbox; - - // up to 3 minutes per test - this.timeout(3 * 60 * 1000); +// up to 3 minutes per test +jest.setTimeout(3 * 60 * 1000); +describe("Packaging commands", () => { let cli: CodeQLCliServer; - let progress: sinon.SinonSpy; - let quickPickSpy: sinon.SinonStub; - let inputBoxSpy: sinon.SinonStub; - let showAndLogErrorMessageSpy: sinon.SinonStub; - let showAndLogInformationMessageSpy: sinon.SinonStub; - let mod: any; + const progress = jest.fn(); + const quickPickSpy = jest.spyOn(window, "showQuickPick"); + const inputBoxSpy = jest.spyOn(window, "showInputBox"); + const showAndLogErrorMessageSpy = jest.spyOn( + helpers, + "showAndLogErrorMessage", + ); + const showAndLogInformationMessageSpy = jest.spyOn( + helpers, + "showAndLogInformationMessage", + ); - beforeEach(async function () { - sandbox = sinon.createSandbox(); - progress = sandbox.spy(); - quickPickSpy = sandbox.stub(window, "showQuickPick"); - inputBoxSpy = sandbox.stub(window, "showInputBox"); - showAndLogErrorMessageSpy = sandbox.stub(); - showAndLogInformationMessageSpy = sandbox.stub(); - mod = proxyquire("../../packaging", { - "./helpers": { - showAndLogErrorMessage: showAndLogErrorMessageSpy, - showAndLogInformationMessage: showAndLogInformationMessageSpy, - }, - }); + beforeEach(async () => { + progress.mockReset(); + quickPickSpy.mockReset().mockResolvedValue(undefined); + inputBoxSpy.mockReset().mockResolvedValue(undefined); + showAndLogErrorMessageSpy.mockReset().mockResolvedValue(undefined); + showAndLogInformationMessageSpy.mockReset().mockResolvedValue(undefined); const extension = await extensions .getExtension>( @@ -51,45 +47,41 @@ describe("Packaging commands", function () { "Extension not initialized. Make sure cli is downloaded and installed properly.", ); } - if (!(await cli.cliConstraints.supportsPackaging())) { - console.log( - `Packaging commands are not supported on CodeQL CLI v${CliVersionConstraint.CLI_VERSION_WITH_PACKAGING}. Skipping this test.`, - ); - this.skip(); - } - }); - - afterEach(() => { - sandbox.restore(); }); it("should download all core query packs", async () => { - quickPickSpy.resolves("Download all core query packs"); + quickPickSpy.mockResolvedValue( + "Download all core query packs" as unknown as QuickPickItem, + ); - await mod.handleDownloadPacks(cli, progress); - expect(showAndLogInformationMessageSpy.firstCall.args[0]).to.contain( - "Finished downloading packs.", + await handleDownloadPacks(cli, progress); + expect(showAndLogInformationMessageSpy).toHaveBeenCalledWith( + expect.stringContaining("Finished downloading packs."), ); }); it("should download valid user-specified pack", async () => { - quickPickSpy.resolves("Download custom specified pack"); - inputBoxSpy.resolves("codeql/csharp-solorigate-queries"); + quickPickSpy.mockResolvedValue( + "Download custom specified pack" as unknown as QuickPickItem, + ); + inputBoxSpy.mockResolvedValue("codeql/csharp-solorigate-queries"); - await mod.handleDownloadPacks(cli, progress); - expect(showAndLogInformationMessageSpy.firstCall.args[0]).to.contain( - "Finished downloading packs.", + await handleDownloadPacks(cli, progress); + expect(showAndLogInformationMessageSpy).toHaveBeenCalledWith( + expect.stringContaining("Finished downloading packs."), ); }); it("should show error when downloading invalid user-specified pack", async () => { - quickPickSpy.resolves("Download custom specified pack"); - inputBoxSpy.resolves("foo/not-a-real-pack@0.0.1"); + quickPickSpy.mockResolvedValue( + "Download custom specified pack" as unknown as QuickPickItem, + ); + inputBoxSpy.mockResolvedValue("foo/not-a-real-pack@0.0.1"); - await mod.handleDownloadPacks(cli, progress); + await handleDownloadPacks(cli, progress); - expect(showAndLogErrorMessageSpy.firstCall.args[0]).to.contain( - "Unable to download all packs.", + expect(showAndLogErrorMessageSpy).toHaveBeenCalledWith( + expect.stringContaining("Unable to download all packs."), ); }); @@ -98,16 +90,16 @@ describe("Packaging commands", function () { __dirname, "../../../src/vscode-tests/cli-integration/data", ); - quickPickSpy.resolves([ + quickPickSpy.mockResolvedValue([ { label: "integration-test-queries-javascript", packRootDir: [rootDir], }, - ]); + ] as unknown as QuickPickItem); - await mod.handleInstallPackDependencies(cli, progress); - expect(showAndLogInformationMessageSpy.firstCall.args[0]).to.contain( - "Finished installing pack dependencies.", + await handleInstallPackDependencies(cli, progress); + expect(showAndLogInformationMessageSpy).toHaveBeenCalledWith( + expect.stringContaining("Finished installing pack dependencies."), ); }); @@ -116,20 +108,20 @@ describe("Packaging commands", function () { __dirname, "../../../src/vscode-tests/cli-integration/data-invalid-pack", ); - quickPickSpy.resolves([ + quickPickSpy.mockResolvedValue([ { label: "foo/bar", packRootDir: [rootDir], }, - ]); + ] as unknown as QuickPickItem); try { // expect this to throw an error - await mod.handleInstallPackDependencies(cli, progress); + await handleInstallPackDependencies(cli, progress); // This line should not be reached - expect(true).to.be.false; + expect(true).toBe(false); } catch (e) { - expect(getErrorMessage(e)).to.contain( + expect(getErrorMessage(e)).toContain( "Unable to install pack dependencies", ); } diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts index 13afcd69f..5a3df451b 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts @@ -6,10 +6,8 @@ import { extensions, Uri, } from "vscode"; -import * as sinon from "sinon"; import * as path from "path"; import * as fs from "fs-extra"; -import { expect } from "chai"; import * as yaml from "js-yaml"; import { DatabaseItem, DatabaseManager } from "../../databases"; @@ -17,27 +15,22 @@ import { CodeQLExtensionInterface } from "../../extension"; import { cleanDatabases, dbLoc, storagePath } from "./global.helper"; import { importArchiveDatabase } from "../../databaseFetcher"; import { CodeQLCliServer } from "../../cli"; -import { skipIfNoCodeQL } from "../ensureCli"; +import { describeWithCodeQL } from "../cli"; import { tmpDir } from "../../helpers"; import { createInitialQueryInfo } from "../../run-queries-shared"; import { QueryRunner } from "../../queryRunner"; +jest.setTimeout(20_000); + /** * Integration tests for queries */ -describe("Queries", function () { - this.timeout(20_000); - - before(function () { - skipIfNoCodeQL(this); - }); - +describeWithCodeQL()("Queries", () => { let dbItem: DatabaseItem; let databaseManager: DatabaseManager; let cli: CodeQLCliServer; let qs: QueryRunner; - let sandbox: sinon.SinonSandbox; - let progress: sinon.SinonSpy; + const progress = jest.fn(); let token: CancellationToken; let ctx: ExtensionContext; @@ -46,11 +39,7 @@ describe("Queries", function () { let oldQlpackLockFile: string; // codeql v2.6.3 and earlier let qlFile: string; - beforeEach(async function () { - this.timeout(20_000); - - sandbox = sinon.createSandbox(); - + beforeEach(async () => { try { const extension = await extensions .getExtension>( @@ -77,7 +66,7 @@ describe("Queries", function () { safeDel(qlFile); safeDel(qlpackFile); - progress = sandbox.spy(); + progress.mockReset(); token = {} as CancellationToken; // Add a database, but make sure the database manager is empty first @@ -101,10 +90,8 @@ describe("Queries", function () { } }); - afterEach(async function () { - this.timeout(20_000); + afterEach(async () => { try { - sandbox.restore(); safeDel(qlpackFile); safeDel(qlFile); await cleanDatabases(databaseManager); @@ -125,7 +112,7 @@ describe("Queries", function () { ); // just check that the query was successful - expect((await result).successful).to.eq(true); + expect((await result).successful).toBe(true); } catch (e) { console.error("Test Failed"); fail(e as Error); @@ -145,7 +132,7 @@ describe("Queries", function () { token, ); - expect(result.successful).to.eq(true); + expect(result.successful).toBe(true); } catch (e) { console.error("Test Failed"); fail(e as Error); @@ -156,14 +143,14 @@ describe("Queries", function () { await commands.executeCommand("codeQL.quickQuery"); // should have created the quick query file and query pack file - expect(fs.pathExistsSync(qlFile)).to.be.true; - expect(fs.pathExistsSync(qlpackFile)).to.be.true; + expect(fs.pathExistsSync(qlFile)).toBe(true); + expect(fs.pathExistsSync(qlpackFile)).toBe(true); const qlpackContents: any = await yaml.load( fs.readFileSync(qlpackFile, "utf8"), ); // Should have chosen the js libraries - expect(qlpackContents.dependencies["codeql/javascript-all"]).to.eq("*"); + expect(qlpackContents.dependencies["codeql/javascript-all"]).toBe("*"); // Should also have a codeql-pack.lock.yml file const packFileToUse = fs.pathExistsSync(qlpackLockFile) @@ -172,8 +159,9 @@ describe("Queries", function () { const qlpackLock: any = await yaml.load( fs.readFileSync(packFileToUse, "utf8"), ); - expect(!!qlpackLock.dependencies["codeql/javascript-all"].version).to.be - .true; + expect(!!qlpackLock.dependencies["codeql/javascript-all"].version).toBe( + true, + ); }); it("should avoid creating a quick query", async () => { @@ -192,7 +180,7 @@ describe("Queries", function () { await commands.executeCommand("codeQL.quickQuery"); // should not have created the quick query file because database schema hasn't changed - expect(fs.readFileSync(qlFile, "utf8")).to.eq("xxx"); + expect(fs.readFileSync(qlFile, "utf8")).toBe("xxx"); }); function safeDel(file: string) { diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts index 71cde8893..157c0a324 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts @@ -1,6 +1,4 @@ -import { assert, expect } from "chai"; import * as path from "path"; -import * as sinon from "sinon"; import { CancellationTokenSource, commands, @@ -14,7 +12,7 @@ import * as os from "os"; import * as yaml from "js-yaml"; import { QlPack } from "../../../remote-queries/run-remote-query"; -import { CliVersionConstraint, CodeQLCliServer } from "../../../cli"; +import { CodeQLCliServer } from "../../../cli"; import { CodeQLExtensionInterface } from "../../../extension"; import { setRemoteControllerRepo, @@ -35,7 +33,10 @@ import { restoreWorkspaceReferences, } from "../global.helper"; -describe("Remote queries", function () { +// up to 3 minutes per test +jest.setTimeout(3 * 60 * 1000); + +describe("Remote queries", () => { const baseDir = path.join( __dirname, "../../../../src/vscode-tests/cli-integration", @@ -45,26 +46,21 @@ describe("Remote queries", function () { "data-remote-qlpack/qlpack.yml", ).fsPath; - let sandbox: sinon.SinonSandbox; - - // up to 3 minutes per test - this.timeout(3 * 60 * 1000); - let cli: CodeQLCliServer; let cancellationTokenSource: CancellationTokenSource; - let progress: sinon.SinonSpy; - let showQuickPickSpy: sinon.SinonStub; - let getRepositoryFromNwoStub: sinon.SinonStub; + const progress = jest.fn(); + const showQuickPickSpy = jest.spyOn(window, "showQuickPick"); + const getRepositoryFromNwoStub = jest.spyOn( + ghApiClient, + "getRepositoryFromNwo", + ); let ctx: ExtensionContext; let logger: any; let remoteQueriesManager: RemoteQueriesManager; let originalDeps: Record | undefined; - // use `function` so we have access to `this` - beforeEach(async function () { - sandbox = sinon.createSandbox(); - + beforeEach(async () => { const extension = await extensions .getExtension>( "GitHub.vscode-codeql", @@ -80,13 +76,6 @@ describe("Remote queries", function () { ctx = createMockExtensionContext(); - if (!(await cli.cliConstraints.supportsRemoteQueries())) { - console.log( - `Remote queries are not supported on CodeQL CLI v${CliVersionConstraint.CLI_VERSION_REMOTE_QUERIES}. Skipping this test.`, - ); - this.skip(); - } - logger = new OutputChannelLogger("test-logger"); remoteQueriesManager = new RemoteQueriesManager( ctx, @@ -97,16 +86,15 @@ describe("Remote queries", function () { cancellationTokenSource = new CancellationTokenSource(); - progress = sandbox.spy(); + progress.mockReset(); + // Should not have asked for a language - showQuickPickSpy = sandbox - .stub(window, "showQuickPick") - .onFirstCall() - .resolves({ + showQuickPickSpy + .mockReset() + .mockResolvedValueOnce({ repositories: ["github/vscode-codeql"], } as unknown as QuickPickItem) - .onSecondCall() - .resolves("javascript" as unknown as QuickPickItem); + .mockResolvedValue("javascript" as unknown as QuickPickItem); const dummyRepository: Repository = { id: 123, @@ -114,9 +102,7 @@ describe("Remote queries", function () { full_name: "github/vscode-codeql", private: false, }; - getRepositoryFromNwoStub = sandbox - .stub(ghApiClient, "getRepositoryFromNwo") - .resolves(dummyRepository); + getRepositoryFromNwoStub.mockReset().mockResolvedValue(dummyRepository); // always run in the vscode-codeql repo await setRemoteControllerRepo("github/vscode-codeql"); @@ -130,7 +116,7 @@ describe("Remote queries", function () { request: undefined, }), } as unknown as Credentials; - sandbox.stub(Credentials, "initialize").resolves(mockCredentials); + jest.spyOn(Credentials, "initialize").mockResolvedValue(mockCredentials); // Only new version support `${workspace}` in qlpack.yml originalDeps = await fixWorkspaceReferences( @@ -140,25 +126,23 @@ describe("Remote queries", function () { }); afterEach(async () => { - sandbox.restore(); await restoreWorkspaceReferences(qlpackFileWithWorkspaceRefs, originalDeps); }); describe("runRemoteQuery", () => { - let mockSubmitRemoteQueries: sinon.SinonStub; - let executeCommandSpy: sinon.SinonStub; + const mockSubmitRemoteQueries = jest.spyOn( + ghApiClient, + "submitRemoteQueries", + ); + const executeCommandSpy = jest.spyOn(commands, "executeCommand"); beforeEach(() => { - executeCommandSpy = sandbox - .stub(commands, "executeCommand") - .callThrough(); + mockSubmitRemoteQueries.mockReset().mockResolvedValue({ + workflow_run_id: 20, + repositories_queried: ["octodemo/hello-world-1"], + }); - mockSubmitRemoteQueries = sandbox - .stub(ghApiClient, "submitRemoteQueries") - .resolves({ - workflow_run_id: 20, - repositories_queried: ["octodemo/hello-world-1"], - }); + executeCommandSpy.mockRestore(); }); it("should run a remote query that is part of a qlpack", async () => { @@ -170,40 +154,40 @@ describe("Remote queries", function () { cancellationTokenSource.token, ); - expect(mockSubmitRemoteQueries).to.have.been.calledOnce; - expect(executeCommandSpy).to.have.been.calledWith( + expect(mockSubmitRemoteQueries).toBeCalledTimes(1); + expect(executeCommandSpy).toBeCalledWith( "codeQL.monitorRemoteQuery", - sinon.match.string, - sinon.match.has("queryFilePath", fileUri.fsPath), + expect.any(String), + expect.objectContaining({ queryFilePath: fileUri.fsPath }), ); const request: RemoteQueriesSubmission = - mockSubmitRemoteQueries.getCall(0).lastArg; + mockSubmitRemoteQueries.mock.calls[0][1]; const packFS = await readBundledPack(request.queryPack); // to retrieve the list of repositories - expect(showQuickPickSpy).to.have.been.calledOnce; + expect(showQuickPickSpy).toBeCalledTimes(1); - expect(getRepositoryFromNwoStub).to.have.been.calledOnce; + expect(getRepositoryFromNwoStub).toBeCalledTimes(1); // check a few files that we know should exist and others that we know should not - expect(packFS.fileExists("in-pack.ql")).to.be.true; - expect(packFS.fileExists("lib.qll")).to.be.true; - expect(packFS.fileExists("qlpack.yml")).to.be.true; + expect(packFS.fileExists("in-pack.ql")).toBe(true); + expect(packFS.fileExists("lib.qll")).toBe(true); + expect(packFS.fileExists("qlpack.yml")).toBe(true); // depending on the cli version, we should have one of these files expect( packFS.fileExists("qlpack.lock.yml") || packFS.fileExists("codeql-pack.lock.yml"), - ).to.be.true; - expect(packFS.fileExists("not-in-pack.ql")).to.be.false; + ).toBe(true); + expect(packFS.fileExists("not-in-pack.ql")).toBe(false); // should have generated a correct qlpack file const qlpackContents: any = yaml.load( packFS.fileContents("qlpack.yml").toString("utf-8"), ); - expect(qlpackContents.name).to.equal("codeql-remote/query"); + expect(qlpackContents.name).toBe("codeql-remote/query"); verifyQlPack( "in-pack.ql", @@ -218,8 +202,8 @@ describe("Remote queries", function () { // check dependencies. // 2.7.4 and earlier have ['javascript-all', 'javascript-upgrades'] // later only have ['javascript-all']. ensure this test can handle either - expect(packNames.length).to.be.lessThan(3).and.greaterThan(0); - expect(packNames[0]).to.deep.equal("javascript-all"); + expect(packNames.length).to.be.lessThan(3).toBeGreaterThan(0); + expect(packNames[0]).toEqual("javascript-all"); }); it("should run a remote query that is not part of a qlpack", async () => { @@ -231,34 +215,34 @@ describe("Remote queries", function () { cancellationTokenSource.token, ); - expect(mockSubmitRemoteQueries).to.have.been.calledOnce; - expect(executeCommandSpy).to.have.been.calledWith( + expect(mockSubmitRemoteQueries).toBeCalledTimes(1); + expect(executeCommandSpy).toBeCalledWith( "codeQL.monitorRemoteQuery", - sinon.match.string, - sinon.match.has("queryFilePath", fileUri.fsPath), + expect.any(String), + expect.objectContaining({ queryFilePath: fileUri.fsPath }), ); const request: RemoteQueriesSubmission = - mockSubmitRemoteQueries.getCall(0).lastArg; + mockSubmitRemoteQueries.mock.calls[0][1]; const packFS = await readBundledPack(request.queryPack); // to retrieve the list of repositories // and a second time to ask for the language - expect(showQuickPickSpy).to.have.been.calledTwice; + expect(showQuickPickSpy).toBeCalledTimes(2); - expect(getRepositoryFromNwoStub).to.have.been.calledOnce; + expect(getRepositoryFromNwoStub).toBeCalledTimes(1); // check a few files that we know should exist and others that we know should not - expect(packFS.fileExists("in-pack.ql")).to.be.true; - expect(packFS.fileExists("qlpack.yml")).to.be.true; + expect(packFS.fileExists("in-pack.ql")).toBe(true); + expect(packFS.fileExists("qlpack.yml")).toBe(true); // depending on the cli version, we should have one of these files expect( packFS.fileExists("qlpack.lock.yml") || packFS.fileExists("codeql-pack.lock.yml"), - ).to.be.true; - expect(packFS.fileExists("lib.qll")).to.be.false; - expect(packFS.fileExists("not-in-pack.ql")).to.be.false; + ).toBe(true); + expect(packFS.fileExists("lib.qll")).toBe(false); + expect(packFS.fileExists("not-in-pack.ql")).toBe(false); // the compiled pack verifyQlPack( @@ -272,11 +256,9 @@ describe("Remote queries", function () { const qlpackContents: any = yaml.load( packFS.fileContents("qlpack.yml").toString("utf-8"), ); - expect(qlpackContents.name).to.equal("codeql-remote/query"); - expect(qlpackContents.version).to.equal("0.0.0"); - expect(qlpackContents.dependencies?.["codeql/javascript-all"]).to.equal( - "*", - ); + expect(qlpackContents.name).toBe("codeql-remote/query"); + expect(qlpackContents.version).toBe("0.0.0"); + expect(qlpackContents.dependencies?.["codeql/javascript-all"]).toBe("*"); const libraryDir = ".codeql/libraries/codeql"; const packNames = packFS.directoryContents(libraryDir).sort(); @@ -284,8 +266,8 @@ describe("Remote queries", function () { // check dependencies. // 2.7.4 and earlier have ['javascript-all', 'javascript-upgrades'] // later only have ['javascript-all']. ensure this test can handle either - expect(packNames.length).to.be.lessThan(3).and.greaterThan(0); - expect(packNames[0]).to.deep.equal("javascript-all"); + expect(packNames.length).to.be.lessThan(3).toBeGreaterThan(0); + expect(packNames[0]).toEqual("javascript-all"); }); it("should run a remote query that is nested inside a qlpack", async () => { @@ -297,33 +279,33 @@ describe("Remote queries", function () { cancellationTokenSource.token, ); - expect(mockSubmitRemoteQueries).to.have.been.calledOnce; - expect(executeCommandSpy).to.have.been.calledWith( + expect(mockSubmitRemoteQueries).toBeCalledTimes(1); + expect(executeCommandSpy).toBeCalledWith( "codeQL.monitorRemoteQuery", - sinon.match.string, - sinon.match.has("queryFilePath", fileUri.fsPath), + expect.any(String), + expect.objectContaining({ queryFilePath: fileUri.fsPath }), ); const request: RemoteQueriesSubmission = - mockSubmitRemoteQueries.getCall(0).lastArg; + mockSubmitRemoteQueries.mock.calls[0][1]; const packFS = await readBundledPack(request.queryPack); // to retrieve the list of repositories - expect(showQuickPickSpy).to.have.been.calledOnce; + expect(showQuickPickSpy).toBeCalledTimes(1); - expect(getRepositoryFromNwoStub).to.have.been.calledOnce; + expect(getRepositoryFromNwoStub).toBeCalledTimes(1); // check a few files that we know should exist and others that we know should not - expect(packFS.fileExists("subfolder/in-pack.ql")).to.be.true; - expect(packFS.fileExists("qlpack.yml")).to.be.true; + expect(packFS.fileExists("subfolder/in-pack.ql")).toBe(true); + expect(packFS.fileExists("qlpack.yml")).toBe(true); // depending on the cli version, we should have one of these files expect( packFS.fileExists("qlpack.lock.yml") || packFS.fileExists("codeql-pack.lock.yml"), - ).to.be.true; - expect(packFS.fileExists("otherfolder/lib.qll")).to.be.true; - expect(packFS.fileExists("not-in-pack.ql")).to.be.false; + ).toBe(true); + expect(packFS.fileExists("otherfolder/lib.qll")).toBe(true); + expect(packFS.fileExists("not-in-pack.ql")).toBe(false); // the compiled pack verifyQlPack( @@ -337,11 +319,9 @@ describe("Remote queries", function () { const qlpackContents: any = yaml.load( packFS.fileContents("qlpack.yml").toString("utf-8"), ); - expect(qlpackContents.name).to.equal("codeql-remote/query"); - expect(qlpackContents.version).to.equal("0.0.0"); - expect(qlpackContents.dependencies?.["codeql/javascript-all"]).to.equal( - "*", - ); + expect(qlpackContents.name).toBe("codeql-remote/query"); + expect(qlpackContents.version).toBe("0.0.0"); + expect(qlpackContents.dependencies?.["codeql/javascript-all"]).toBe("*"); const libraryDir = ".codeql/libraries/codeql"; const packNames = packFS.directoryContents(libraryDir).sort(); @@ -349,8 +329,8 @@ describe("Remote queries", function () { // check dependencies. // 2.7.4 and earlier have ['javascript-all', 'javascript-upgrades'] // later only have ['javascript-all']. ensure this test can handle either - expect(packNames.length).to.be.lessThan(3).and.greaterThan(0); - expect(packNames[0]).to.deep.equal("javascript-all"); + expect(packNames.length).to.be.lessThan(3).toBeGreaterThan(0); + expect(packNames[0]).toEqual("javascript-all"); }); it("should cancel a run before uploading", async () => { @@ -366,9 +346,9 @@ describe("Remote queries", function () { try { await promise; - assert.fail("should have thrown"); + fail("should have thrown"); } catch (e) { - expect(e).to.be.instanceof(UserCancellationException); + expect(e).toBeInstanceOf(UserCancellationException); } }); }); @@ -389,7 +369,7 @@ describe("Remote queries", function () { // don't check the build metadata since it is variable delete (qlPack as any).buildMetadata; - expect(qlPack).to.deep.equal({ + expect(qlPack).toEqual({ name: "codeql-remote/query", version: packVersion, dependencies: { diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts index f986cf8a8..00eb2f6d0 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts @@ -1,5 +1,3 @@ -import * as sinon from "sinon"; -import { assert, expect } from "chai"; import { CancellationTokenSource, commands, @@ -23,7 +21,7 @@ import * as fs from "fs-extra"; import * as path from "path"; import { VariantAnalysisManager } from "../../../remote-queries/variant-analysis-manager"; -import { CliVersionConstraint, CodeQLCliServer } from "../../../cli"; +import { CodeQLCliServer } from "../../../cli"; import { fixWorkspaceReferences, restoreWorkspaceReferences, @@ -56,31 +54,31 @@ import { SortKey, } from "../../../pure/variant-analysis-filter-sort"; -describe("Variant Analysis Manager", async function () { - let sandbox: sinon.SinonSandbox; - let pathExistsStub: sinon.SinonStub; - let readJsonStub: sinon.SinonStub; - let outputJsonStub: sinon.SinonStub; - let writeFileStub: sinon.SinonStub; +// up to 3 minutes per test +jest.setTimeout(3 * 60 * 1000); + +describe("Variant Analysis Manager", async () => { + const pathExistsStub = jest.spyOn(fs, "pathExists"); + const readJsonStub = jest.spyOn(fs, "readJson"); + const outputJsonStub = jest.spyOn(fs, "outputJson"); + const writeFileStub = jest.spyOn(fs, "writeFile"); let cli: CodeQLCliServer; let cancellationTokenSource: CancellationTokenSource; let variantAnalysisManager: VariantAnalysisManager; + let variantAnalysisResultsManager: VariantAnalysisResultsManager; let variantAnalysis: VariantAnalysis; let scannedRepos: VariantAnalysisScannedRepository[]; - let getVariantAnalysisRepoStub: sinon.SinonStub; - let getVariantAnalysisRepoResultStub: sinon.SinonStub; - let variantAnalysisResultsManager: VariantAnalysisResultsManager; - let originalDeps: Record | undefined; beforeEach(async () => { - sandbox = sinon.createSandbox(); - sandbox.stub(logger, "log"); - sandbox.stub(config, "isVariantAnalysisLiveResultsEnabled").returns(false); - sandbox.stub(fs, "mkdirSync"); - writeFileStub = sandbox.stub(fs, "writeFile"); - pathExistsStub = sandbox.stub(fs, "pathExists").callThrough(); - readJsonStub = sandbox.stub(fs, "readJson").callThrough(); - outputJsonStub = sandbox.stub(fs, "outputJson"); + jest.spyOn(logger, "log").mockResolvedValue(undefined); + jest + .spyOn(config, "isVariantAnalysisLiveResultsEnabled") + .mockReturnValue(false); + jest.spyOn(fs, "mkdirSync").mockReturnValue(undefined); + writeFileStub.mockReturnValue(undefined); + pathExistsStub.mockRestore(); + readJsonStub.mockRestore(); + outputJsonStub.mockReturnValue(undefined); cancellationTokenSource = new CancellationTokenSource(); @@ -112,20 +110,20 @@ describe("Variant Analysis Manager", async function () { } }); - afterEach(async () => { - sandbox.restore(); - }); - - describe("runVariantAnalysis", function () { - // up to 3 minutes per test - this.timeout(3 * 60 * 1000); - - let progress: sinon.SinonSpy; - let showQuickPickSpy: sinon.SinonStub; - let mockGetRepositoryFromNwo: sinon.SinonStub; - let mockSubmitVariantAnalysis: sinon.SinonStub; + describe("runVariantAnalysis", () => { + const progress = jest.fn(); + const showQuickPickSpy = jest.spyOn(window, "showQuickPick"); + const mockGetRepositoryFromNwo = jest.spyOn( + ghApiClient, + "getRepositoryFromNwo", + ); + const mockSubmitVariantAnalysis = jest.spyOn( + ghApiClient, + "submitVariantAnalysis", + ); let mockApiResponse: VariantAnalysisApiResponse; - let executeCommandSpy: sinon.SinonStub; + let originalDeps: Record | undefined; + const executeCommandSpy = jest.spyOn(commands, "executeCommand"); const baseDir = path.join( __dirname, @@ -139,30 +137,19 @@ describe("Variant Analysis Manager", async function () { return Uri.file(path.join(baseDir, file)); } - beforeEach(async function () { - if (!(await cli.cliConstraints.supportsRemoteQueries())) { - console.log( - `Remote queries are not supported on CodeQL CLI v${CliVersionConstraint.CLI_VERSION_REMOTE_QUERIES}. Skipping this test.`, - ); - this.skip(); - } + beforeEach(async () => { + writeFileStub.mockRestore(); - writeFileStub.callThrough(); - - progress = sandbox.spy(); + progress.mockReset(); // Should not have asked for a language - showQuickPickSpy = sandbox - .stub(window, "showQuickPick") - .onFirstCall() - .resolves({ + showQuickPickSpy + .mockReset() + .mockResolvedValueOnce({ repositories: ["github/vscode-codeql"], } as unknown as QuickPickItem) - .onSecondCall() - .resolves("javascript" as unknown as QuickPickItem); + .mockResolvedValueOnce("javascript" as unknown as QuickPickItem); - executeCommandSpy = sandbox - .stub(commands, "executeCommand") - .callThrough(); + executeCommandSpy.mockRestore(); cancellationTokenSource = new CancellationTokenSource(); @@ -172,14 +159,10 @@ describe("Variant Analysis Manager", async function () { full_name: "github/vscode-codeql", private: false, }; - mockGetRepositoryFromNwo = sandbox - .stub(ghApiClient, "getRepositoryFromNwo") - .resolves(dummyRepository); + mockGetRepositoryFromNwo.mockReset().mockResolvedValue(dummyRepository); mockApiResponse = createMockApiResponse("in_progress"); - mockSubmitVariantAnalysis = sandbox - .stub(ghApiClient, "submitVariantAnalysis") - .resolves(mockApiResponse); + mockSubmitVariantAnalysis.mockReset().mockResolvedValue(mockApiResponse); // always run in the vscode-codeql repo await setRemoteControllerRepo("github/vscode-codeql"); @@ -210,17 +193,18 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - expect(executeCommandSpy).to.have.been.calledWith( + expect(executeCommandSpy).toBeCalledWith( "codeQL.monitorVariantAnalysis", - sinon.match - .has("id", mockApiResponse.id) - .and(sinon.match.has("status", VariantAnalysisStatus.InProgress)), + expect.objectContaining({ + id: mockApiResponse.id, + status: VariantAnalysisStatus.InProgress, + }), ); - expect(showQuickPickSpy).to.have.been.calledOnce; + expect(showQuickPickSpy).toBeCalledTimes(1); - expect(mockGetRepositoryFromNwo).to.have.been.calledOnce; - expect(mockSubmitVariantAnalysis).to.have.been.calledOnce; + expect(mockGetRepositoryFromNwo).toBeCalledTimes(1); + expect(mockSubmitVariantAnalysis).toBeCalledTimes(1); }); it("should run a remote query that is not part of a qlpack", async () => { @@ -232,15 +216,16 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - expect(executeCommandSpy).to.have.been.calledWith( + expect(executeCommandSpy).toBeCalledWith( "codeQL.monitorVariantAnalysis", - sinon.match - .has("id", mockApiResponse.id) - .and(sinon.match.has("status", VariantAnalysisStatus.InProgress)), + expect.objectContaining({ + id: mockApiResponse.id, + status: VariantAnalysisStatus.InProgress, + }), ); - expect(mockGetRepositoryFromNwo).to.have.been.calledOnce; - expect(mockSubmitVariantAnalysis).to.have.been.calledOnce; + expect(mockGetRepositoryFromNwo).toBeCalledTimes(1); + expect(mockSubmitVariantAnalysis).toBeCalledTimes(1); }); it("should run a remote query that is nested inside a qlpack", async () => { @@ -252,15 +237,16 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - expect(executeCommandSpy).to.have.been.calledWith( + expect(executeCommandSpy).toBeCalledWith( "codeQL.monitorVariantAnalysis", - sinon.match - .has("id", mockApiResponse.id) - .and(sinon.match.has("status", VariantAnalysisStatus.InProgress)), + expect.objectContaining({ + id: mockApiResponse.id, + status: VariantAnalysisStatus.InProgress, + }), ); - expect(mockGetRepositoryFromNwo).to.have.been.calledOnce; - expect(mockSubmitVariantAnalysis).to.have.been.calledOnce; + expect(mockGetRepositoryFromNwo).toBeCalledTimes(1); + expect(mockSubmitVariantAnalysis).toBeCalledTimes(1); }); it("should cancel a run before uploading", async () => { @@ -276,9 +262,9 @@ describe("Variant Analysis Manager", async function () { try { await promise; - assert.fail("should have thrown"); + fail("should have thrown"); } catch (e) { - expect(e).to.be.instanceof(UserCancellationException); + expect(e).toBeInstanceOf(UserCancellationException); } }); }); @@ -288,20 +274,25 @@ describe("Variant Analysis Manager", async function () { describe("when the directory does not exist", () => { beforeEach(() => { - pathExistsStub - .withArgs(path.join(storagePath, variantAnalysis.id.toString())) - .resolves(false); + const originalFs = jest.requireActual("fs-extras"); + pathExistsStub.mockReset().mockImplementation((...args) => { + if ( + args[0] === path.join(storagePath, variantAnalysis.id.toString()) + ) { + return false; + } + return originalFs.pathExists(...args); + }); }); it("should fire the removed event if the file does not exist", async () => { - const stub = sandbox.stub(); + const stub = jest.fn(); variantAnalysisManager.onVariantAnalysisRemoved(stub); await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); - expect(stub).to.have.been.calledOnce; - sinon.assert.calledWith( - pathExistsStub, + expect(stub).toBeCalledTimes(1); + expect(pathExistsStub).toBeCalledWith( path.join(storagePath, variantAnalysis.id.toString()), ); }); @@ -309,9 +300,15 @@ describe("Variant Analysis Manager", async function () { describe("when the directory exists", () => { beforeEach(() => { - pathExistsStub - .withArgs(path.join(storagePath, variantAnalysis.id.toString())) - .resolves(true); + const originalFs = jest.requireActual("fs-extras"); + pathExistsStub.mockReset().mockImplementation((...args) => { + if ( + args[0] === path.join(storagePath, variantAnalysis.id.toString()) + ) { + return true; + } + return originalFs.pathExists(...args); + }); }); it("should store the variant analysis", async () => { @@ -319,24 +316,28 @@ describe("Variant Analysis Manager", async function () { expect( await variantAnalysisManager.getVariantAnalysis(variantAnalysis.id), - ).to.deep.equal(variantAnalysis); + ).toEqual(variantAnalysis); }); it("should not error if the repo states file does not exist", async () => { - readJsonStub - .withArgs( + const originalFs = jest.requireActual("fs-extras"); + readJsonStub.mockImplementation((...args) => { + if ( + args[0] === path.join( storagePath, variantAnalysis.id.toString(), "repo_states.json", - ), - ) - .rejects(new Error("File does not exist")); + ) + ) { + return Promise.reject(new Error("File does not exist")); + } + return originalFs.readJson(...args); + }); await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); - sinon.assert.calledWith( - readJsonStub, + expect(readJsonStub).toHaveBeenCalledWith( path.join( storagePath, variantAnalysis.id.toString(), @@ -345,62 +346,70 @@ describe("Variant Analysis Manager", async function () { ); expect( await variantAnalysisManager.getRepoStates(variantAnalysis.id), - ).to.deep.equal([]); + ).toEqual([]); }); it("should read in the repo states if it exists", async () => { - readJsonStub - .withArgs( + const originalFs = jest.requireActual("fs-extras"); + readJsonStub.mockImplementation((...args) => { + if ( + args[0] === path.join( storagePath, variantAnalysis.id.toString(), "repo_states.json", - ), - ) - .resolves({ - [scannedRepos[0].repository.id]: { + ) + ) { + return Promise.resolve({ + [scannedRepos[0].repository.id]: { + repositoryId: scannedRepos[0].repository.id, + downloadStatus: + VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, + }, + [scannedRepos[1].repository.id]: { + repositoryId: scannedRepos[1].repository.id, + downloadStatus: + VariantAnalysisScannedRepositoryDownloadStatus.InProgress, + }, + }); + } + return originalFs.readJson(...args); + }); + + await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); + + expect(readJsonStub).toHaveBeenCalledWith( + path.join( + storagePath, + variantAnalysis.id.toString(), + "repo_states.json", + ), + ); + expect( + await variantAnalysisManager.getRepoStates(variantAnalysis.id), + ).toEqual( + expect.arrayContaining([ + { repositoryId: scannedRepos[0].repository.id, downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, }, - [scannedRepos[1].repository.id]: { + { repositoryId: scannedRepos[1].repository.id, downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.InProgress, }, - }); - - await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); - - sinon.assert.calledWith( - readJsonStub, - path.join( - storagePath, - variantAnalysis.id.toString(), - "repo_states.json", - ), + ]), ); - expect( - await variantAnalysisManager.getRepoStates(variantAnalysis.id), - ).to.have.same.deep.members([ - { - repositoryId: scannedRepos[0].repository.id, - downloadStatus: - VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, - }, - { - repositoryId: scannedRepos[1].repository.id, - downloadStatus: - VariantAnalysisScannedRepositoryDownloadStatus.InProgress, - }, - ]); }); }); }); describe("when credentials are invalid", async () => { beforeEach(async () => { - sandbox.stub(Credentials, "initialize").resolves(undefined); + jest + .spyOn(Credentials, "initialize") + .mockResolvedValue(undefined as unknown as Credentials); }); it("should return early if credentials are wrong", async () => { @@ -411,29 +420,40 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); } catch (error: any) { - expect(error.message).to.equal("Error authenticating with GitHub"); + expect(error.message).toBe("Error authenticating with GitHub"); } }); }); describe("when credentials are valid", async () => { - let getOctokitStub: sinon.SinonStub; let arrayBuffer: ArrayBuffer; + const getVariantAnalysisRepoStub = jest.spyOn( + ghApiClient, + "getVariantAnalysisRepo", + ); + const getVariantAnalysisRepoResultStub = jest.spyOn( + ghApiClient, + "getVariantAnalysisRepoResult", + ); + beforeEach(async () => { const mockCredentials = { getOctokit: () => Promise.resolve({ - request: getOctokitStub, + request: jest.fn(), }), } as unknown as Credentials; - sandbox.stub(Credentials, "initialize").resolves(mockCredentials); + jest.spyOn(Credentials, "initialize").mockResolvedValue(mockCredentials); const sourceFilePath = path.join( __dirname, "../../../../src/vscode-tests/cli-integration/data/variant-analysis-results.zip", ); arrayBuffer = fs.readFileSync(sourceFilePath).buffer; + + getVariantAnalysisRepoStub.mockReset(); + getVariantAnalysisRepoResultStub.mockReset(); }); describe("when the artifact_url is missing", async () => { @@ -441,12 +461,8 @@ describe("Variant Analysis Manager", async function () { const dummyRepoTask = createMockVariantAnalysisRepoTask(); delete dummyRepoTask.artifact_url; - getVariantAnalysisRepoStub = sandbox - .stub(ghApiClient, "getVariantAnalysisRepo") - .resolves(dummyRepoTask); - getVariantAnalysisRepoResultStub = sandbox - .stub(ghApiClient, "getVariantAnalysisRepoResult") - .resolves(arrayBuffer); + getVariantAnalysisRepoStub.mockResolvedValue(dummyRepoTask); + getVariantAnalysisRepoResultStub.mockResolvedValue(arrayBuffer); }); it("should not try to download the result", async () => { @@ -456,7 +472,7 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - expect(getVariantAnalysisRepoResultStub.notCalled).to.be.true; + expect(getVariantAnalysisRepoResultStub).not.toHaveBeenCalled(); }); }); @@ -466,12 +482,8 @@ describe("Variant Analysis Manager", async function () { beforeEach(async () => { dummyRepoTask = createMockVariantAnalysisRepoTask(); - getVariantAnalysisRepoStub = sandbox - .stub(ghApiClient, "getVariantAnalysisRepo") - .resolves(dummyRepoTask); - getVariantAnalysisRepoResultStub = sandbox - .stub(ghApiClient, "getVariantAnalysisRepoResult") - .resolves(arrayBuffer); + getVariantAnalysisRepoStub.mockResolvedValue(dummyRepoTask); + getVariantAnalysisRepoResultStub.mockResolvedValue(arrayBuffer); }); describe("autoDownloadVariantAnalysisResult", async () => { @@ -484,7 +496,7 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - expect(getVariantAnalysisRepoStub.notCalled).to.be.true; + expect(getVariantAnalysisRepoStub).not.toHaveBeenCalled(); }); it("should fetch a repo task", async () => { @@ -494,7 +506,7 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - expect(getVariantAnalysisRepoStub.calledOnce).to.be.true; + expect(getVariantAnalysisRepoStub).toHaveBeenCalled(); }); it("should fetch a repo result", async () => { @@ -504,7 +516,7 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - expect(getVariantAnalysisRepoResultStub.calledOnce).to.be.true; + expect(getVariantAnalysisRepoResultStub).toHaveBeenCalled(); }); it("should skip the download if the repository has already been downloaded", async () => { @@ -515,7 +527,7 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - getVariantAnalysisRepoStub.resetHistory(); + getVariantAnalysisRepoStub.mockClear(); await variantAnalysisManager.autoDownloadVariantAnalysisResult( scannedRepos[0], @@ -523,7 +535,7 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - expect(getVariantAnalysisRepoStub.notCalled).to.be.true; + expect(getVariantAnalysisRepoStub).not.toHaveBeenCalled(); }); it("should write the repo state when the download is successful", async () => { @@ -533,8 +545,7 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - sinon.assert.calledWith( - outputJsonStub, + expect(outputJsonStub).toHaveBeenCalledWith( path.join( storagePath, variantAnalysis.id.toString(), @@ -551,7 +562,7 @@ describe("Variant Analysis Manager", async function () { }); it("should not write the repo state when the download fails", async () => { - getVariantAnalysisRepoResultStub.rejects( + getVariantAnalysisRepoResultStub.mockRejectedValue( new Error("Failed to download"), ); @@ -566,13 +577,13 @@ describe("Variant Analysis Manager", async function () { // we can ignore this error, we expect this } - sinon.assert.notCalled(outputJsonStub); + expect(outputJsonStub).not.toHaveBeenCalled(); }); it("should have a failed repo state when the repo task API fails", async () => { - getVariantAnalysisRepoStub - .onFirstCall() - .rejects(new Error("Failed to download")); + getVariantAnalysisRepoStub.mockRejectedValueOnce( + new Error("Failed to download"), + ); try { await variantAnalysisManager.autoDownloadVariantAnalysisResult( @@ -585,7 +596,7 @@ describe("Variant Analysis Manager", async function () { // we can ignore this error, we expect this } - sinon.assert.notCalled(outputJsonStub); + expect(outputJsonStub).not.toHaveBeenCalled(); await variantAnalysisManager.autoDownloadVariantAnalysisResult( scannedRepos[1], @@ -593,8 +604,7 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - sinon.assert.calledWith( - outputJsonStub, + expect(outputJsonStub).toHaveBeenCalledWith( path.join( storagePath, variantAnalysis.id.toString(), @@ -616,9 +626,9 @@ describe("Variant Analysis Manager", async function () { }); it("should have a failed repo state when the download fails", async () => { - getVariantAnalysisRepoResultStub - .onFirstCall() - .rejects(new Error("Failed to download")); + getVariantAnalysisRepoResultStub.mockRejectedValueOnce( + new Error("Failed to download"), + ); try { await variantAnalysisManager.autoDownloadVariantAnalysisResult( @@ -631,7 +641,7 @@ describe("Variant Analysis Manager", async function () { // we can ignore this error, we expect this } - sinon.assert.notCalled(outputJsonStub); + expect(outputJsonStub).not.toHaveBeenCalled(); await variantAnalysisManager.autoDownloadVariantAnalysisResult( scannedRepos[1], @@ -639,8 +649,7 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - sinon.assert.calledWith( - outputJsonStub, + expect(outputJsonStub).toHaveBeenCalledWith( path.join( storagePath, variantAnalysis.id.toString(), @@ -666,36 +675,45 @@ describe("Variant Analysis Manager", async function () { // The actual tests for these are in rehydrateVariantAnalysis, so we can just mock them here and test that // the methods are called. - pathExistsStub - .withArgs(path.join(storagePath, variantAnalysis.id.toString())) - .resolves(true); + const originalFs = jest.requireActual("fs-extras"); + pathExistsStub.mockReset().mockImplementation((...args) => { + if ( + args[0] === path.join(storagePath, variantAnalysis.id.toString()) + ) { + return false; + } + return originalFs.pathExists(...args); + }); // This will read in the correct repo states - readJsonStub - .withArgs( + readJsonStub.mockImplementation((...args) => { + if ( + args[0] === path.join( storagePath, variantAnalysis.id.toString(), "repo_states.json", - ), - ) - .resolves({ - [scannedRepos[1].repository.id]: { - repositoryId: scannedRepos[1].repository.id, - downloadStatus: - VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, - }, - [scannedRepos[2].repository.id]: { - repositoryId: scannedRepos[2].repository.id, - downloadStatus: - VariantAnalysisScannedRepositoryDownloadStatus.InProgress, - }, - }); + ) + ) { + return Promise.resolve({ + [scannedRepos[1].repository.id]: { + repositoryId: scannedRepos[1].repository.id, + downloadStatus: + VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, + }, + [scannedRepos[2].repository.id]: { + repositoryId: scannedRepos[2].repository.id, + downloadStatus: + VariantAnalysisScannedRepositoryDownloadStatus.InProgress, + }, + }); + } + return originalFs.readJson(...args); + }); await variantAnalysisManager.rehydrateVariantAnalysis( variantAnalysis, ); - sinon.assert.calledWith( - readJsonStub, + expect(readJsonStub).toHaveBeenCalledWith( path.join( storagePath, variantAnalysis.id.toString(), @@ -709,8 +727,7 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - sinon.assert.calledWith( - outputJsonStub, + expect(outputJsonStub).toHaveBeenCalledWith( path.join( storagePath, variantAnalysis.id.toString(), @@ -739,7 +756,7 @@ describe("Variant Analysis Manager", async function () { describe("enqueueDownload", async () => { it("should pop download tasks off the queue", async () => { - const getResultsSpy = sandbox.spy( + const getResultsSpy = jest.spyOn( variantAnalysisManager, "autoDownloadVariantAnalysisResult", ); @@ -760,38 +777,38 @@ describe("Variant Analysis Manager", async function () { cancellationTokenSource.token, ); - expect(variantAnalysisManager.downloadsQueueSize()).to.equal(0); - expect(getResultsSpy).to.have.been.calledThrice; + expect(variantAnalysisManager.downloadsQueueSize()).toBe(0); + expect(getResultsSpy).toBeCalledTimes(3); }); }); describe("removeVariantAnalysis", async () => { - let removeAnalysisResultsStub: sinon.SinonStub; - let removeStorageStub: sinon.SinonStub; + const removeAnalysisResultsStub = jest.spyOn( + variantAnalysisResultsManager, + "removeAnalysisResults", + ); + const removeStorageStub = jest.spyOn(fs, "remove"); let dummyVariantAnalysis: VariantAnalysis; beforeEach(async () => { dummyVariantAnalysis = createMockVariantAnalysis({}); - removeAnalysisResultsStub = sandbox.stub( - variantAnalysisResultsManager, - "removeAnalysisResults", - ); - removeStorageStub = sandbox.stub(fs, "remove"); + removeAnalysisResultsStub.mockReset().mockReturnValue(undefined); + removeStorageStub.mockReset().mockReturnValue(undefined); }); it("should remove variant analysis", async () => { await variantAnalysisManager.onVariantAnalysisUpdated( dummyVariantAnalysis, ); - expect(variantAnalysisManager.variantAnalysesSize).to.eq(1); + expect(variantAnalysisManager.variantAnalysesSize).toBe(1); await variantAnalysisManager.removeVariantAnalysis( dummyVariantAnalysis, ); - expect(removeAnalysisResultsStub).to.have.been.calledOnce; - expect(removeStorageStub).to.have.been.calledOnce; - expect(variantAnalysisManager.variantAnalysesSize).to.equal(0); + expect(removeAnalysisResultsStub).toBeCalledTimes(1); + expect(removeStorageStub).toBeCalledTimes(1); + expect(variantAnalysisManager.variantAnalysesSize).toBe(0); }); }); }); @@ -799,32 +816,29 @@ describe("Variant Analysis Manager", async function () { describe("when rehydrating a query", async () => { let variantAnalysis: VariantAnalysis; - let variantAnalysisRemovedSpy: sinon.SinonSpy; - let monitorVariantAnalysisCommandSpy: sinon.SinonSpy; + const variantAnalysisRemovedSpy = jest.fn(); + const executeCommandSpy = jest.spyOn(commands, "executeCommand"); beforeEach(() => { variantAnalysis = createMockVariantAnalysis({}); - variantAnalysisRemovedSpy = sinon.spy(); + variantAnalysisRemovedSpy.mockReset(); variantAnalysisManager.onVariantAnalysisRemoved( variantAnalysisRemovedSpy, ); - monitorVariantAnalysisCommandSpy = sinon.spy(); - sandbox - .stub(commands, "executeCommand") - .callsFake(monitorVariantAnalysisCommandSpy); + executeCommandSpy.mockReset().mockResolvedValue(undefined); }); describe("when variant analysis record doesn't exist", async () => { it("should remove the variant analysis", async () => { await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); - sinon.assert.calledOnce(variantAnalysisRemovedSpy); + expect(variantAnalysisRemovedSpy).toHaveBeenCalledTimes(1); }); it("should not trigger a monitoring command", async () => { await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); - sinon.assert.notCalled(monitorVariantAnalysisCommandSpy); + expect(executeCommandSpy).not.toHaveBeenCalled(); }); }); @@ -845,48 +859,48 @@ describe("Variant Analysis Manager", async function () { describe("when the variant analysis is not complete", async () => { beforeEach(() => { - sandbox - .stub(VariantAnalysisModule, "isVariantAnalysisComplete") - .resolves(false); + jest + .spyOn(VariantAnalysisModule, "isVariantAnalysisComplete") + .mockResolvedValue(false); }); it("should not remove the variant analysis", async () => { await variantAnalysisManager.rehydrateVariantAnalysis( variantAnalysis, ); - sinon.assert.notCalled(variantAnalysisRemovedSpy); + expect(variantAnalysisRemovedSpy).not.toHaveBeenCalled(); }); it("should trigger a monitoring command", async () => { await variantAnalysisManager.rehydrateVariantAnalysis( variantAnalysis, ); - sinon.assert.calledWith( - monitorVariantAnalysisCommandSpy, + expect(executeCommandSpy).toHaveBeenCalledWith( "codeQL.monitorVariantAnalysis", + expect.anything(), ); }); }); describe("when the variant analysis is complete", async () => { beforeEach(() => { - sandbox - .stub(VariantAnalysisModule, "isVariantAnalysisComplete") - .resolves(true); + jest + .spyOn(VariantAnalysisModule, "isVariantAnalysisComplete") + .mockResolvedValue(true); }); it("should not remove the variant analysis", async () => { await variantAnalysisManager.rehydrateVariantAnalysis( variantAnalysis, ); - sinon.assert.notCalled(variantAnalysisRemovedSpy); + expect(variantAnalysisRemovedSpy).not.toHaveBeenCalled(); }); it("should not trigger a monitoring command", async () => { await variantAnalysisManager.rehydrateVariantAnalysis( variantAnalysis, ); - sinon.assert.notCalled(monitorVariantAnalysisCommandSpy); + expect(executeCommandSpy).not.toHaveBeenCalled(); }); }); }); @@ -894,18 +908,17 @@ describe("Variant Analysis Manager", async function () { describe("cancelVariantAnalysis", async () => { let variantAnalysis: VariantAnalysis; - let mockCancelVariantAnalysis: sinon.SinonStub; - let getOctokitStub: sinon.SinonStub; + const mockCancelVariantAnalysis = jest.spyOn( + ghActionsApiClient, + "cancelVariantAnalysis", + ); let variantAnalysisStorageLocation: string; beforeEach(async () => { variantAnalysis = createMockVariantAnalysis({}); - mockCancelVariantAnalysis = sandbox.stub( - ghActionsApiClient, - "cancelVariantAnalysis", - ); + mockCancelVariantAnalysis.mockReset().mockResolvedValue(undefined); variantAnalysisStorageLocation = variantAnalysisManager.getVariantAnalysisStorageLocation( @@ -921,7 +934,9 @@ describe("Variant Analysis Manager", async function () { describe("when the credentials are invalid", () => { beforeEach(async () => { - sandbox.stub(Credentials, "initialize").resolves(undefined); + jest + .spyOn(Credentials, "initialize") + .mockResolvedValue(undefined as unknown as Credentials); }); it("should return early", async () => { @@ -930,7 +945,7 @@ describe("Variant Analysis Manager", async function () { variantAnalysis.id, ); } catch (error: any) { - expect(error.message).to.equal("Error authenticating with GitHub"); + expect(error.message).toBe("Error authenticating with GitHub"); } }); }); @@ -942,10 +957,12 @@ describe("Variant Analysis Manager", async function () { mockCredentials = { getOctokit: () => Promise.resolve({ - request: getOctokitStub, + request: jest.fn(), }), } as unknown as Credentials; - sandbox.stub(Credentials, "initialize").resolves(mockCredentials); + jest + .spyOn(Credentials, "initialize") + .mockResolvedValue(mockCredentials); }); it("should return early if the variant analysis is not found", async () => { @@ -954,7 +971,7 @@ describe("Variant Analysis Manager", async function () { variantAnalysis.id + 100, ); } catch (error: any) { - expect(error.message).to.equal( + expect(error.message).toBe( "No variant analysis with id: " + (variantAnalysis.id + 100), ); } @@ -971,7 +988,7 @@ describe("Variant Analysis Manager", async function () { variantAnalysis.id, ); } catch (error: any) { - expect(error.message).to.equal( + expect(error.message).toBe( `No workflow run id for variant analysis with id: ${variantAnalysis.id}`, ); } @@ -980,7 +997,7 @@ describe("Variant Analysis Manager", async function () { it("should return cancel if valid", async () => { await variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id); - expect(mockCancelVariantAnalysis).to.have.been.calledWith( + expect(mockCancelVariantAnalysis).toBeCalledWith( mockCredentials, variantAnalysis, ); @@ -992,7 +1009,7 @@ describe("Variant Analysis Manager", async function () { let variantAnalysis: VariantAnalysis; let variantAnalysisStorageLocation: string; - let writeTextStub: sinon.SinonStub; + const writeTextStub = jest.fn(); beforeEach(async () => { variantAnalysis = createMockVariantAnalysis({}); @@ -1004,8 +1021,9 @@ describe("Variant Analysis Manager", async function () { await createTimestampFile(variantAnalysisStorageLocation); await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); - writeTextStub = sinon.stub(); - sinon.stub(env, "clipboard").value({ + writeTextStub.mockReset(); + jest.spyOn(env, "clipboard", "get").mockReturnValue({ + readText: jest.fn(), writeText: writeTextStub, }); }); @@ -1027,7 +1045,7 @@ describe("Variant Analysis Manager", async function () { variantAnalysis.id, ); - expect(writeTextStub).not.to.have.been.called; + expect(writeTextStub).not.toBeCalled(); }); }); @@ -1053,7 +1071,7 @@ describe("Variant Analysis Manager", async function () { variantAnalysis.id, ); - expect(writeTextStub).not.to.have.been.called; + expect(writeTextStub).not.toBeCalled(); }); }); @@ -1093,7 +1111,7 @@ describe("Variant Analysis Manager", async function () { variantAnalysis.id, ); - expect(writeTextStub).to.have.been.calledOnce; + expect(writeTextStub).toBeCalledTimes(1); }); it("should be valid JSON when put in object", async () => { @@ -1101,11 +1119,11 @@ describe("Variant Analysis Manager", async function () { variantAnalysis.id, ); - const text = writeTextStub.getCalls()[0].lastArg; + const text = writeTextStub.mock.calls[0][0]; const parsed = JSON.parse("{" + text + "}"); - expect(parsed).to.deep.eq({ + expect(parsed).toEqual({ "new-repo-list": [ scannedRepos[4].repository.fullName, scannedRepos[2].repository.fullName, @@ -1123,11 +1141,11 @@ describe("Variant Analysis Manager", async function () { }, ); - const text = writeTextStub.getCalls()[0].lastArg; + const text = writeTextStub.mock.calls[0][0]; const parsed = JSON.parse("{" + text + "}"); - expect(parsed).to.deep.eq({ + expect(parsed).toEqual({ "new-repo-list": [ scannedRepos[2].repository.fullName, scannedRepos[0].repository.fullName, @@ -1145,11 +1163,11 @@ describe("Variant Analysis Manager", async function () { }, ); - const text = writeTextStub.getCalls()[0].lastArg; + const text = writeTextStub.mock.calls[0][0]; const parsed = JSON.parse("{" + text + "}"); - expect(parsed).to.deep.eq({ + expect(parsed).toEqual({ "new-repo-list": [scannedRepos[4].repository.fullName], }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts index 6b4db96aa..03bd39c0a 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts @@ -1,6 +1,9 @@ -import * as sinon from "sinon"; -import { expect } from "chai"; -import { CancellationTokenSource, commands, extensions } from "vscode"; +import { + CancellationToken, + CancellationTokenSource, + commands, + extensions, +} from "vscode"; import { CodeQLExtensionInterface } from "../../../extension"; import * as config from "../../../config"; @@ -17,6 +20,7 @@ import { } from "../../factories/remote-queries/gh-api/variant-analysis-api-response"; import { VariantAnalysis, + VariantAnalysisScannedRepository, VariantAnalysisStatus, } from "../../../remote-queries/shared/variant-analysis"; import { createMockScannedRepos } from "../../factories/remote-queries/gh-api/scanned-repositories"; @@ -29,21 +33,28 @@ import { Credentials } from "../../../authentication"; import { createMockVariantAnalysis } from "../../factories/remote-queries/shared/variant-analysis"; import { VariantAnalysisManager } from "../../../remote-queries/variant-analysis-manager"; -describe("Variant Analysis Monitor", async function () { - this.timeout(60000); +jest.setTimeout(60_000); - let sandbox: sinon.SinonSandbox; +describe("Variant Analysis Monitor", async () => { let extension: CodeQLExtensionInterface | Record; - let mockGetVariantAnalysis: sinon.SinonStub; + const mockGetVariantAnalysis = jest.spyOn(ghApiClient, "getVariantAnalysis"); let cancellationTokenSource: CancellationTokenSource; let variantAnalysisMonitor: VariantAnalysisMonitor; let variantAnalysis: VariantAnalysis; let variantAnalysisManager: VariantAnalysisManager; - let mockGetDownloadResult: sinon.SinonStub; + let mockGetDownloadResult: jest.SpyInstance< + Promise, + [ + scannedRepo: VariantAnalysisScannedRepository, + variantAnalysis: VariantAnalysis, + cancellationToken: CancellationToken, + ] + >; beforeEach(async () => { - sandbox = sinon.createSandbox(); - sandbox.stub(config, "isVariantAnalysisLiveResultsEnabled").returns(false); + jest + .spyOn(config, "isVariantAnalysisLiveResultsEnabled") + .mockReturnValue(false); cancellationTokenSource = new CancellationTokenSource(); @@ -61,21 +72,22 @@ describe("Variant Analysis Monitor", async function () { } variantAnalysisManager = extension.variantAnalysisManager; - mockGetDownloadResult = sandbox.stub( - variantAnalysisManager, - "autoDownloadVariantAnalysisResult", - ); + mockGetDownloadResult = jest + .spyOn(variantAnalysisManager, "autoDownloadVariantAnalysisResult") + .mockResolvedValue(undefined); + + mockGetVariantAnalysis + .mockReset() + .mockRejectedValue(new Error("Not mocked")); limitNumberOfAttemptsToMonitor(); }); - afterEach(async () => { - sandbox.restore(); - }); - describe("when credentials are invalid", async () => { beforeEach(async () => { - sandbox.stub(Credentials, "initialize").resolves(undefined); + jest + .spyOn(Credentials, "initialize") + .mockResolvedValue(undefined as unknown as Credentials); }); it("should return early if credentials are wrong", async () => { @@ -85,7 +97,7 @@ describe("Variant Analysis Monitor", async function () { cancellationTokenSource.token, ); } catch (error: any) { - expect(error.message).to.equal("Error authenticating with GitHub"); + expect(error.message).toBe("Error authenticating with GitHub"); } }); }); @@ -95,10 +107,10 @@ describe("Variant Analysis Monitor", async function () { const mockCredentials = { getOctokit: () => Promise.resolve({ - request: mockGetVariantAnalysis, + request: jest.fn(), }), } as unknown as Credentials; - sandbox.stub(Credentials, "initialize").resolves(mockCredentials); + jest.spyOn(Credentials, "initialize").mockResolvedValue(mockCredentials); }); it("should return early if variant analysis is cancelled", async () => { @@ -109,17 +121,15 @@ describe("Variant Analysis Monitor", async function () { cancellationTokenSource.token, ); - expect(result).to.eql({ status: "Canceled" }); + expect(result).toEqual({ status: "Canceled" }); }); describe("when the variant analysis fails", async () => { let mockFailedApiResponse: VariantAnalysisApiResponse; - beforeEach(async function () { + beforeEach(async () => { mockFailedApiResponse = createFailedMockApiResponse(); - mockGetVariantAnalysis = sandbox - .stub(ghApiClient, "getVariantAnalysis") - .resolves(mockFailedApiResponse); + mockGetVariantAnalysis.mockResolvedValue(mockFailedApiResponse); }); it("should mark as failed locally and stop monitoring", async () => { @@ -128,12 +138,12 @@ describe("Variant Analysis Monitor", async function () { cancellationTokenSource.token, ); - expect(mockGetVariantAnalysis.calledOnce).to.be.true; - expect(result.status).to.eql("Completed"); - expect(result.variantAnalysis?.status).to.equal( + expect(mockGetVariantAnalysis).toHaveBeenCalledTimes(1); + expect(result.status).toEqual("Completed"); + expect(result.variantAnalysis?.status).toBe( VariantAnalysisStatus.Failed, ); - expect(result.variantAnalysis?.failureReason).to.equal( + expect(result.variantAnalysis?.failureReason).toBe( processFailureReason( mockFailedApiResponse.failure_reason as VariantAnalysisFailureReason, ), @@ -141,7 +151,7 @@ describe("Variant Analysis Monitor", async function () { }); it("should emit `onVariantAnalysisChange`", async () => { - const spy = sandbox.spy(); + const spy = jest.fn(); variantAnalysisMonitor.onVariantAnalysisChange(spy); const result = await variantAnalysisMonitor.monitorVariantAnalysis( @@ -149,7 +159,7 @@ describe("Variant Analysis Monitor", async function () { cancellationTokenSource.token, ); - expect(spy).to.have.been.calledWith(result.variantAnalysis); + expect(spy).toBeCalledWith(result.variantAnalysis); }); }); @@ -159,7 +169,7 @@ describe("Variant Analysis Monitor", async function () { let succeededRepos: ApiVariantAnalysisScannedRepository[]; describe("when there are successfully scanned repos", async () => { - beforeEach(async function () { + beforeEach(async () => { scannedRepos = createMockScannedRepos([ "pending", "pending", @@ -170,9 +180,7 @@ describe("Variant Analysis Monitor", async function () { "succeeded", ]); mockApiResponse = createMockApiResponse("succeeded", scannedRepos); - mockGetVariantAnalysis = sandbox - .stub(ghApiClient, "getVariantAnalysis") - .resolves(mockApiResponse); + mockGetVariantAnalysis.mockResolvedValue(mockApiResponse); succeededRepos = scannedRepos.filter( (r) => r.analysis_status === "succeeded", ); @@ -184,8 +192,8 @@ describe("Variant Analysis Monitor", async function () { cancellationTokenSource.token, ); - expect(result.status).to.equal("Completed"); - expect(result.scannedReposDownloaded).to.eql( + expect(result.status).toBe("Completed"); + expect(result.scannedReposDownloaded).toEqual( succeededRepos.map((r) => r.repository.id), ); }); @@ -194,23 +202,22 @@ describe("Variant Analysis Monitor", async function () { const succeededRepos = scannedRepos.filter( (r) => r.analysis_status === "succeeded", ); - const commandSpy = sandbox.spy(commands, "executeCommand"); + const commandSpy = jest + .spyOn(commands, "executeCommand") + .mockResolvedValue(undefined); await variantAnalysisMonitor.monitorVariantAnalysis( variantAnalysis, cancellationTokenSource.token, ); - expect(commandSpy).to.have.callCount(succeededRepos.length); + expect(commandSpy).toBeCalledTimes(succeededRepos.length); succeededRepos.forEach((succeededRepo, index) => { - expect(commandSpy.getCall(index).args[0]).to.eq( + expect(commandSpy).toHaveBeenNthCalledWith( + index + 1, "codeQL.autoDownloadVariantAnalysisResult", - ); - expect(commandSpy.getCall(index).args[1]).to.deep.eq( processScannedRepository(succeededRepo), - ); - expect(commandSpy.getCall(index).args[2]).to.deep.eq( processUpdatedVariantAnalysis(variantAnalysis, mockApiResponse), ); }); @@ -222,15 +229,12 @@ describe("Variant Analysis Monitor", async function () { cancellationTokenSource.token, ); - expect(mockGetDownloadResult).to.have.callCount( - succeededRepos.length, - ); + expect(mockGetDownloadResult).toBeCalledTimes(succeededRepos.length); succeededRepos.forEach((succeededRepo, index) => { - expect(mockGetDownloadResult.getCall(index).args[0]).to.deep.eq( + expect(mockGetDownloadResult).toHaveBeenNthCalledWith( + index + 1, processScannedRepository(succeededRepo), - ); - expect(mockGetDownloadResult.getCall(index).args[1]).to.deep.eq( processUpdatedVariantAnalysis(variantAnalysis, mockApiResponse), ); }); @@ -240,12 +244,10 @@ describe("Variant Analysis Monitor", async function () { describe("when there are only in progress repos", async () => { let scannedRepos: ApiVariantAnalysisScannedRepository[]; - beforeEach(async function () { + beforeEach(async () => { scannedRepos = createMockScannedRepos(["pending", "in_progress"]); mockApiResponse = createMockApiResponse("in_progress", scannedRepos); - mockGetVariantAnalysis = sandbox - .stub(ghApiClient, "getVariantAnalysis") - .resolves(mockApiResponse); + mockGetVariantAnalysis.mockResolvedValue(mockApiResponse); }); it("should succeed and return an empty list of scanned repo ids", async () => { @@ -254,8 +256,8 @@ describe("Variant Analysis Monitor", async function () { cancellationTokenSource.token, ); - expect(result.status).to.equal("Completed"); - expect(result.scannedReposDownloaded).to.eql([]); + expect(result.status).toBe("Completed"); + expect(result.scannedReposDownloaded).toEqual([]); }); it("should not try to download any repos", async () => { @@ -264,17 +266,15 @@ describe("Variant Analysis Monitor", async function () { cancellationTokenSource.token, ); - expect(mockGetDownloadResult).to.not.have.been.called; + expect(mockGetDownloadResult).not.toBeCalled(); }); }); describe("when there are no repos to scan", async () => { - beforeEach(async function () { + beforeEach(async () => { scannedRepos = []; mockApiResponse = createMockApiResponse("succeeded", scannedRepos); - mockGetVariantAnalysis = sandbox - .stub(ghApiClient, "getVariantAnalysis") - .resolves(mockApiResponse); + mockGetVariantAnalysis.mockResolvedValue(mockApiResponse); }); it("should succeed and return an empty list of scanned repo ids", async () => { @@ -283,8 +283,8 @@ describe("Variant Analysis Monitor", async function () { cancellationTokenSource.token, ); - expect(result.status).to.equal("Completed"); - expect(result.scannedReposDownloaded).to.eql([]); + expect(result.status).toBe("Completed"); + expect(result.scannedReposDownloaded).toEqual([]); }); it("should not try to download any repos", async () => { @@ -293,7 +293,7 @@ describe("Variant Analysis Monitor", async function () { cancellationTokenSource.token, ); - expect(mockGetDownloadResult).to.not.have.been.called; + expect(mockGetDownloadResult).not.toBeCalled(); }); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts index 8dccacc86..af8c32396 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts @@ -1,5 +1,3 @@ -import * as sinon from "sinon"; -import { expect } from "chai"; import { extensions } from "vscode"; import { CodeQLExtensionInterface } from "../../../extension"; import { logger } from "../../../logging"; @@ -15,20 +13,17 @@ import * as ghApiClient from "../../../remote-queries/gh-api/gh-api-client"; import { createMockVariantAnalysisRepositoryTask } from "../../factories/remote-queries/shared/variant-analysis-repo-tasks"; import { VariantAnalysisRepositoryTask } from "../../../remote-queries/shared/variant-analysis"; -describe(VariantAnalysisResultsManager.name, function () { - this.timeout(10000); +jest.setTimeout(10_000); - let sandbox: sinon.SinonSandbox; +describe(VariantAnalysisResultsManager.name, () => { let cli: CodeQLCliServer; let variantAnalysisId: number; let variantAnalysisResultsManager: VariantAnalysisResultsManager; - let getVariantAnalysisRepoResultStub: sinon.SinonStub; beforeEach(async () => { - sandbox = sinon.createSandbox(); - sandbox.stub(logger, "log"); - sandbox.stub(fs, "mkdirSync"); - sandbox.stub(fs, "writeFile"); + jest.spyOn(logger, "log").mockResolvedValue(undefined); + jest.spyOn(fs, "mkdirSync").mockReturnValue(undefined); + jest.spyOn(fs, "writeFile").mockReturnValue(undefined); variantAnalysisId = faker.datatype.number(); @@ -48,16 +43,11 @@ describe(VariantAnalysisResultsManager.name, function () { } }); - afterEach(async () => { - sandbox.restore(); - }); - describe("download", () => { - let getOctokitStub: sinon.SinonStub; const mockCredentials = { getOctokit: () => Promise.resolve({ - request: getOctokitStub, + request: jest.fn(), }), } as unknown as Credentials; let dummyRepoTask: VariantAnalysisRepositoryTask; @@ -91,7 +81,7 @@ describe(VariantAnalysisResultsManager.name, function () { variantAnalysisStoragePath, dummyRepoTask.repository.fullName, ), - ).to.equal(false); + ).toBe(false); }); }); @@ -108,9 +98,9 @@ describe(VariantAnalysisResultsManager.name, function () { variantAnalysisStoragePath, ); - expect.fail("Expected an error to be thrown"); + fail("Expected an error to be thrown"); } catch (e: any) { - expect(e.message).to.equal("Missing artifact URL"); + expect(e.message).toBe("Missing artifact URL"); } }); }); @@ -118,6 +108,11 @@ describe(VariantAnalysisResultsManager.name, function () { describe("when the artifact_url is present", async () => { let arrayBuffer: ArrayBuffer; + const getVariantAnalysisRepoResultStub = jest.spyOn( + ghApiClient, + "getVariantAnalysisRepoResult", + ); + beforeEach(async () => { const sourceFilePath = path.join( __dirname, @@ -125,10 +120,16 @@ describe(VariantAnalysisResultsManager.name, function () { ); arrayBuffer = fs.readFileSync(sourceFilePath).buffer; - getVariantAnalysisRepoResultStub = sandbox - .stub(ghApiClient, "getVariantAnalysisRepoResult") - .withArgs(mockCredentials, dummyRepoTask.artifactUrl as string) - .resolves(arrayBuffer); + getVariantAnalysisRepoResultStub + .mockReset() + .mockImplementation( + (_credentials: Credentials, downloadUrl: string) => { + if (downloadUrl === dummyRepoTask.artifactUrl) { + return Promise.resolve(arrayBuffer); + } + return Promise.reject(new Error("Unexpected artifact URL")); + }, + ); }); it("should call the API to download the results", async () => { @@ -139,7 +140,7 @@ describe(VariantAnalysisResultsManager.name, function () { variantAnalysisStoragePath, ); - expect(getVariantAnalysisRepoResultStub.calledOnce).to.be.true; + expect(getVariantAnalysisRepoResultStub).toHaveBeenCalledTimes(1); }); it("should save the results zip file to disk", async () => { @@ -150,8 +151,9 @@ describe(VariantAnalysisResultsManager.name, function () { variantAnalysisStoragePath, ); - expect(fs.existsSync(`${repoTaskStorageDirectory}/results.zip`)).to.be - .true; + expect(fs.existsSync(`${repoTaskStorageDirectory}/results.zip`)).toBe( + true, + ); }); it("should unzip the results in a `results/` folder", async () => { @@ -164,7 +166,7 @@ describe(VariantAnalysisResultsManager.name, function () { expect( fs.existsSync(`${repoTaskStorageDirectory}/results/results.sarif`), - ).to.be.true; + ).toBe(true); }); describe("isVariantAnalysisRepoDownloaded", () => { @@ -181,7 +183,7 @@ describe(VariantAnalysisResultsManager.name, function () { variantAnalysisStoragePath, dummyRepoTask.repository.fullName, ), - ).to.equal(true); + ).toBe(true); }); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts index 0d4c57351..15e2cc4fb 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts @@ -1,8 +1,13 @@ import * as path from "path"; -import * as sinon from "sinon"; - -import { commands, extensions, TextDocument, window, workspace } from "vscode"; +import { + commands, + extensions, + QuickPickItem, + TextDocument, + window, + workspace, +} from "vscode"; import * as Octokit from "@octokit/rest"; import { retry } from "@octokit/plugin-retry"; @@ -11,10 +16,12 @@ import * as config from "../../../config"; import { Credentials } from "../../../authentication"; import { MockGitHubApiServer } from "../../../mocks/mock-gh-api-server"; +jest.setTimeout(10_000); + const mockServer = new MockGitHubApiServer(); -before(() => mockServer.startServer()); +beforeAll(() => mockServer.startServer()); afterEach(() => mockServer.unloadScenario()); -after(() => mockServer.stopServer()); +afterAll(() => mockServer.stopServer()); async function showQlDocument(name: string): Promise { const folderPath = workspace.workspaceFolders![0].uri.fsPath; @@ -24,35 +31,30 @@ async function showQlDocument(name: string): Promise { return document; } -describe("Variant Analysis Submission Integration", function () { - this.timeout(10_000); - - let sandbox: sinon.SinonSandbox; - let quickPickSpy: sinon.SinonStub; - let inputBoxSpy: sinon.SinonStub; - let executeCommandSpy: sinon.SinonStub; - let showErrorMessageSpy: sinon.SinonStub; +describe("Variant Analysis Submission Integration", () => { + const quickPickSpy = jest.spyOn(window, "showQuickPick"); + const inputBoxSpy = jest.spyOn(window, "showInputBox"); + const executeCommandSpy = jest.spyOn(commands, "executeCommand"); + const showErrorMessageSpy = jest.spyOn(window, "showErrorMessage"); beforeEach(async () => { - sandbox = sinon.createSandbox(); - - sandbox.stub(config, "isCanary").returns(true); - sandbox.stub(config, "isVariantAnalysisLiveResultsEnabled").returns(true); + jest.spyOn(config, "isCanary").mockReturnValue(true); + jest + .spyOn(config, "isVariantAnalysisLiveResultsEnabled") + .mockReturnValue(true); const mockCredentials = { getOctokit: () => Promise.resolve(new Octokit.Octokit({ retry })), } as unknown as Credentials; - sandbox.stub(Credentials, "initialize").resolves(mockCredentials); + jest.spyOn(Credentials, "initialize").mockResolvedValue(mockCredentials); await config.setRemoteControllerRepo("github/vscode-codeql"); - quickPickSpy = sandbox.stub(window, "showQuickPick").resolves(undefined); - inputBoxSpy = sandbox.stub(window, "showInputBox").resolves(undefined); + quickPickSpy.mockReset().mockResolvedValue(undefined); + inputBoxSpy.mockReset().mockResolvedValue(undefined); - executeCommandSpy = sandbox.stub(commands, "executeCommand").callThrough(); - showErrorMessageSpy = sandbox - .stub(window, "showErrorMessage") - .resolves(undefined); + executeCommandSpy.mockRestore(); + showErrorMessageSpy.mockReset().mockResolvedValue(undefined); try { await extensions @@ -65,10 +67,6 @@ describe("Variant Analysis Submission Integration", function () { } }); - afterEach(() => { - sandbox.restore(); - }); - describe("Successful scenario", () => { beforeEach(async () => { await mockServer.loadScenario("problem-query-success"); @@ -78,18 +76,19 @@ describe("Variant Analysis Submission Integration", function () { await showQlDocument("query.ql"); // Select a repository list - quickPickSpy.onFirstCall().resolves({ + quickPickSpy.mockResolvedValueOnce({ useCustomRepo: true, - }); + } as unknown as QuickPickItem); // Enter a GitHub repository - inputBoxSpy.onFirstCall().resolves("github/codeql"); + inputBoxSpy.mockResolvedValueOnce("github/codeql"); // Select target language for your query - quickPickSpy.onSecondCall().resolves("javascript"); + quickPickSpy.mockResolvedValueOnce( + "javascript" as unknown as QuickPickItem, + ); await commands.executeCommand("codeQL.runVariantAnalysis"); - sinon.assert.calledWith( - executeCommandSpy, + expect(executeCommandSpy).toHaveBeenCalledWith( "codeQL.openVariantAnalysisView", 146, ); @@ -105,18 +104,19 @@ describe("Variant Analysis Submission Integration", function () { await showQlDocument("query.ql"); // Select a repository list - quickPickSpy.onFirstCall().resolves({ + quickPickSpy.mockResolvedValueOnce({ useCustomRepo: true, - }); + } as unknown as QuickPickItem); // Enter a GitHub repository - inputBoxSpy.onFirstCall().resolves("github/codeql"); + inputBoxSpy.mockResolvedValueOnce("github/codeql"); await commands.executeCommand("codeQL.runVariantAnalysis"); - sinon.assert.calledWith( - showErrorMessageSpy, - sinon.match('Controller repository "github/vscode-codeql" not found'), - sinon.match.string, + expect(showErrorMessageSpy).toHaveBeenCalledWith( + expect.stringContaining( + 'Controller repository "github/vscode-codeql" not found', + ), + expect.any(String), ); }); }); @@ -130,20 +130,21 @@ describe("Variant Analysis Submission Integration", function () { await showQlDocument("query.ql"); // Select a repository list - quickPickSpy.onFirstCall().resolves({ + quickPickSpy.mockResolvedValueOnce({ useCustomRepo: true, - }); + } as unknown as QuickPickItem); // Enter a GitHub repository - inputBoxSpy.onFirstCall().resolves("github/codeql"); + inputBoxSpy.mockResolvedValueOnce("github/codeql"); // Select target language for your query - quickPickSpy.onSecondCall().resolves("javascript"); + quickPickSpy.mockResolvedValueOnce( + "javascript" as unknown as QuickPickItem, + ); await commands.executeCommand("codeQL.runVariantAnalysis"); - sinon.assert.calledWith( - showErrorMessageSpy, - sinon.match("No repositories could be queried."), - sinon.match.string, + expect(showErrorMessageSpy).toHaveBeenCalledWith( + expect.stringContaining("No repositories could be queried."), + expect.any(String), ); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts index bccf63ab5..e913440f4 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts @@ -1,11 +1,10 @@ -import { expect } from "chai"; import { extensions, Uri } from "vscode"; import * as path from "path"; import { SemVer } from "semver"; import { CodeQLCliServer, QueryInfoByLanguage } from "../../cli"; import { CodeQLExtensionInterface } from "../../extension"; -import { skipIfNoCodeQL } from "../ensureCli"; +import { itWithCodeQL } from "../cli"; import { getOnDiskWorkspaceFolders, getQlPackForDbscheme, @@ -15,12 +14,12 @@ import { resolveQueries } from "../../contextual/queryResolver"; import { KeyType } from "../../contextual/keyType"; import { fail } from "assert"; +jest.setTimeout(60_000); + /** * Perform proper integration tests by running the CLI */ -describe("Use cli", function () { - this.timeout(60000); - +describe("Use cli", () => { let cli: CodeQLCliServer; let supportedLanguages: string[]; @@ -42,7 +41,7 @@ describe("Use cli", function () { if (process.env.CLI_VERSION && process.env.CLI_VERSION !== "nightly") { it("should have the correct version of the cli", async () => { - expect((await cli.getVersion()).toString()).to.eq( + expect((await cli.getVersion()).toString()).toBe( new SemVer(process.env.CLI_VERSION || "").toString(), ); }); @@ -50,11 +49,10 @@ describe("Use cli", function () { it("should resolve ram", async () => { const result = await (cli as any).resolveRam(8192); - expect(result).to.deep.eq(["-J-Xmx4096M", "--off-heap-ram=4096"]); + expect(result).toEqual(["-J-Xmx4096M", "--off-heap-ram=4096"]); }); - it("should resolve query packs", async function () { - skipIfNoCodeQL(this); + itWithCodeQL()("should resolve query packs", async () => { const qlpacks = await cli.resolveQlpacks(getOnDiskWorkspaceFolders()); // Depending on the version of the CLI, the qlpacks may have different names // (e.g. "codeql/javascript-all" vs "codeql-javascript"), @@ -64,19 +62,17 @@ describe("Use cli", function () { } }); - it("should support the expected languages", async function () { - skipIfNoCodeQL(this); + itWithCodeQL()("should support the expected languages", async () => { // Just check a few examples that definitely are/aren't supported. - expect(supportedLanguages).to.include.members([ - "go", - "javascript", - "python", - ]); - expect(supportedLanguages).to.not.include.members(["xml", "properties"]); + expect(supportedLanguages).toEqual( + expect.arrayContaining(["go", "javascript", "python"]), + ); + expect(supportedLanguages).not.toEqual( + expect.arrayContaining(["xml", "properties"]), + ); }); - it("should resolve query by language", async function () { - skipIfNoCodeQL(this); + itWithCodeQL()("should resolve query by language", async () => { const queryPath = path.join( __dirname, "data", @@ -86,33 +82,38 @@ describe("Use cli", function () { getOnDiskWorkspaceFolders(), Uri.file(queryPath), ); - expect(Object.keys(queryInfo.byLanguage)[0]).to.eql("javascript"); + expect(Object.keys(queryInfo.byLanguage)[0]).toEqual("javascript"); }); - it("should resolve printAST queries for supported languages", async function () { - skipIfNoCodeQL(this); - try { - for (const lang of supportedLanguages) { - if (lang === "go") { - // The codeql-go submodule is not available in the integration tests. - return; + itWithCodeQL()( + "should resolve printAST queries for supported languages", + async () => { + try { + for (const lang of supportedLanguages) { + if (lang === "go") { + // The codeql-go submodule is not available in the integration tests. + return; + } + + console.log(`resolving printAST queries for ${lang}`); + const pack = await getQlPackForDbscheme( + cli, + languageToDbScheme[lang], + ); + expect(pack.dbschemePack).toEqual(expect.arrayContaining([lang])); + if (pack.dbschemePackIsLibraryPack) { + expect(pack.queryPack).toEqual(expect.arrayContaining([lang])); + } + + const result = await resolveQueries(cli, pack, KeyType.PrintAstQuery); + + // It doesn't matter what the name or path of the query is, only + // that we have found exactly one query. + expect(result.length).toBe(1); } - - console.log(`resolving printAST queries for ${lang}`); - const pack = await getQlPackForDbscheme(cli, languageToDbScheme[lang]); - expect(pack.dbschemePack).to.contain(lang); - if (pack.dbschemePackIsLibraryPack) { - expect(pack.queryPack).to.contain(lang); - } - - const result = await resolveQueries(cli, pack, KeyType.PrintAstQuery); - - // It doesn't matter what the name or path of the query is, only - // that we have found exactly one query. - expect(result.length).to.eq(1); + } catch (e) { + fail(e as Error); } - } catch (e) { - fail(e as Error); - } - }); + }, + ); }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/sourcemap.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/sourcemap.test.ts index 81281be75..e174db109 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/sourcemap.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/sourcemap.test.ts @@ -2,16 +2,15 @@ import { fail } from "assert"; import { commands, Selection, window, workspace } from "vscode"; import * as path from "path"; import * as assert from "assert"; -import { expect } from "chai"; import { tmpDir } from "../../helpers"; import * as fs from "fs-extra"; +jest.setTimeout(20_000); + /** * Integration tests for queries */ -describe("SourceMap", function () { - this.timeout(20000); - +describe("SourceMap", () => { it("should jump to QL code", async () => { try { const root = workspace.workspaceFolders![0].uri.fsPath; @@ -41,12 +40,12 @@ describe("SourceMap", function () { await commands.executeCommand("codeQL.gotoQL"); const newEditor = window.activeTextEditor; - expect(newEditor).to.be.not.undefined; + expect(newEditor).toBeDefined(); const newDocument = newEditor!.document; - expect(path.basename(newDocument.fileName)).to.equal("Namespace.qll"); + expect(path.basename(newDocument.fileName)).toBe("Namespace.qll"); const newSelection = newEditor!.selection; - expect(newSelection.start.line).to.equal(60); - expect(newSelection.start.character).to.equal(2); + expect(newSelection.start.line).toBe(60); + expect(newSelection.start.character).toBe(2); } catch (e) { console.error("Test Failed"); fail(e as Error); diff --git a/extensions/ql-vscode/src/vscode-tests/cli.ts b/extensions/ql-vscode/src/vscode-tests/cli.ts new file mode 100644 index 000000000..e875c44ea --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/cli.ts @@ -0,0 +1,44 @@ +import { workspace } from "vscode"; + +/** + * Heuristically determines if the codeql libraries are installed in this + * workspace. Looks for the existance of a folder whose path ends in `/codeql` + */ +function hasCodeQL() { + const folders = workspace.workspaceFolders; + return !!folders?.some((folder) => folder.uri.path.endsWith("/codeql")); +} + +// describeWithCodeQL will be equal to describe if the CodeQL libraries are +// available in this workspace. Otherwise, it will skip the tests. +export function describeWithCodeQL() { + if (!hasCodeQL()) { + console.log( + [ + "The CodeQL libraries are not available as a folder in this workspace.", + "To fix in CI: checkout the github/codeql repository and set the 'TEST_CODEQL_PATH' environment variable to the checked out directory.", + "To fix when running from vs code, see the comment in the launch.json file in the 'Launch Integration Tests - With CLI' section.", + ].join("\n\n"), + ); + return describe.skip; + } + + return describe; +} + +// itWithCodeQL will be equal to it if the CodeQL libraries are +// available in this workspace. Otherwise, it will skip the tests. +export function itWithCodeQL() { + if (!hasCodeQL()) { + console.log( + [ + "The CodeQL libraries are not available as a folder in this workspace.", + "To fix in CI: checkout the github/codeql repository and set the 'TEST_CODEQL_PATH' environment variable to the checked out directory.", + "To fix when running from vs code, see the comment in the launch.json file in the 'Launch Integration Tests - With CLI' section.", + ].join("\n\n"), + ); + return it.skip; + } + + return it; +} diff --git a/extensions/ql-vscode/src/vscode-tests/ensureCli.ts b/extensions/ql-vscode/src/vscode-tests/ensureCli.ts index b9130e821..aecac097d 100644 --- a/extensions/ql-vscode/src/vscode-tests/ensureCli.ts +++ b/extensions/ql-vscode/src/vscode-tests/ensureCli.ts @@ -1,12 +1,11 @@ import * as fs from "fs-extra"; import * as path from "path"; import { - DistributionManager, + getRequiredAssetName, extractZipArchive, codeQlLauncherName, -} from "../distribution"; +} from "../pure/distribution"; import fetch from "node-fetch"; -import { workspace } from "vscode"; /** * This module ensures that the proper CLI is available for tests of the extension. @@ -64,7 +63,7 @@ export async function ensureCli(useCli: boolean) { return; } - const assetName = DistributionManager.getRequiredAssetName(); + const assetName = getRequiredAssetName(); const url = getCliDownloadUrl(assetName); const unzipDir = getCliUnzipDir(); const downloadedFilePath = getDownloadFilePath(assetName); @@ -136,28 +135,6 @@ export async function ensureCli(useCli: boolean) { } } -/** - * Heuristically determines if the codeql libraries are installed in this - * workspace. Looks for the existance of a folder whose path ends in `/codeql` - */ -function hasCodeQL() { - const folders = workspace.workspaceFolders; - return !!folders?.some((folder) => folder.uri.path.endsWith("/codeql")); -} - -export function skipIfNoCodeQL(context: Mocha.Context) { - if (!hasCodeQL()) { - console.log( - [ - "The CodeQL libraries are not available as a folder in this workspace.", - "To fix in CI: checkout the github/codeql repository and set the 'TEST_CODEQL_PATH' environment variable to the checked out directory.", - "To fix when running from vs code, see the comment in the launch.json file in the 'Launch Integration Tests - With CLI' section.", - ].join("\n\n"), - ); - context.skip(); - } -} - /** * Url to download from */ diff --git a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts index 9e5e1f778..c390eceb2 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts +++ b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts @@ -9,8 +9,8 @@ export const rootDir = path.resolve(__dirname, "../.."); const config: RunnerOptions = { version: "stable", launchArgs: [ - "--disable-extensions", "--disable-gpu", + "--extensions-dir=" + path.join(rootDir, ".vscode-test", "extensions"), "--user-data-dir=" + path.join(tmpDir.name, "user-data"), ], extensionDevelopmentPath: rootDir, diff --git a/extensions/ql-vscode/src/vscode-tests/jest.setup.ts b/extensions/ql-vscode/src/vscode-tests/jest.setup.ts index a35117880..86334151c 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest.setup.ts +++ b/extensions/ql-vscode/src/vscode-tests/jest.setup.ts @@ -1,5 +1,17 @@ import { env } from "vscode"; +import { jestTestConfigHelper } from "./test-config"; (env as any).openExternal = () => { /**/ }; + +function fail(reason = "fail was called in a test.") { + throw new Error(reason); +} + +// Jest doesn't seem to define this function anymore, but it's in the types, so should be valid. +(global as any).fail = fail; + +export default async function setupEnv() { + await jestTestConfigHelper(); +} diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts index 066444e18..0d0108474 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts @@ -4,6 +4,7 @@ import baseConfig from "../jest-runner-vscode.config.base"; const config: RunnerOptions = { ...baseConfig, + launchArgs: [...(baseConfig.launchArgs ?? []), "--disable-extensions"], }; // We are purposefully not using export default here since that would result in an ESModule, which doesn't seem to be diff --git a/extensions/ql-vscode/src/vscode-tests/test-config.ts b/extensions/ql-vscode/src/vscode-tests/test-config.ts index 76b2fd187..4bb9ac371 100644 --- a/extensions/ql-vscode/src/vscode-tests/test-config.ts +++ b/extensions/ql-vscode/src/vscode-tests/test-config.ts @@ -127,3 +127,23 @@ export const testConfigHelper = async (mocha: Mocha) => { }, }); }; + +export const jestTestConfigHelper = async () => { + // Read in all current settings + await Promise.all(TEST_SETTINGS.map((setting) => setting.initialSetup())); + + beforeEach(async () => { + // Reset the settings to their initial values before each test + await Promise.all(TEST_SETTINGS.map((setting) => setting.setup())); + }); + + afterAll(async () => { + // Restore all settings to their default values after each test suite + // Only do this outside of CI since the sometimes hangs on CI. + if (process.env.CI !== "true") { + await Promise.all( + TEST_SETTINGS.map((setting) => setting.restoreToInitialValues()), + ); + } + }); +}; From dc6de0f8a8df4e7b8010ee90c8133d8d5998650e Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 23 Nov 2022 16:57:16 +0100 Subject: [PATCH 14/58] Fix async describes --- .../variant-analysis-manager.test.ts | 30 +++++++++---------- .../variant-analysis-monitor.test.ts | 16 +++++----- .../variant-analysis-results-manager.test.ts | 4 +-- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts index 00eb2f6d0..89149918d 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts @@ -57,7 +57,7 @@ import { // up to 3 minutes per test jest.setTimeout(3 * 60 * 1000); -describe("Variant Analysis Manager", async () => { +describe("Variant Analysis Manager", () => { const pathExistsStub = jest.spyOn(fs, "pathExists"); const readJsonStub = jest.spyOn(fs, "readJson"); const outputJsonStub = jest.spyOn(fs, "outputJson"); @@ -405,7 +405,7 @@ describe("Variant Analysis Manager", async () => { }); }); - describe("when credentials are invalid", async () => { + describe("when credentials are invalid", () => { beforeEach(async () => { jest .spyOn(Credentials, "initialize") @@ -425,7 +425,7 @@ describe("Variant Analysis Manager", async () => { }); }); - describe("when credentials are valid", async () => { + describe("when credentials are valid", () => { let arrayBuffer: ArrayBuffer; const getVariantAnalysisRepoStub = jest.spyOn( @@ -456,7 +456,7 @@ describe("Variant Analysis Manager", async () => { getVariantAnalysisRepoResultStub.mockReset(); }); - describe("when the artifact_url is missing", async () => { + describe("when the artifact_url is missing", () => { beforeEach(async () => { const dummyRepoTask = createMockVariantAnalysisRepoTask(); delete dummyRepoTask.artifact_url; @@ -476,7 +476,7 @@ describe("Variant Analysis Manager", async () => { }); }); - describe("when the artifact_url is present", async () => { + describe("when the artifact_url is present", () => { let dummyRepoTask: VariantAnalysisRepoTask; beforeEach(async () => { @@ -486,7 +486,7 @@ describe("Variant Analysis Manager", async () => { getVariantAnalysisRepoResultStub.mockResolvedValue(arrayBuffer); }); - describe("autoDownloadVariantAnalysisResult", async () => { + describe("autoDownloadVariantAnalysisResult", () => { it("should return early if variant analysis is cancelled", async () => { cancellationTokenSource.cancel(); @@ -754,7 +754,7 @@ describe("Variant Analysis Manager", async () => { }); }); - describe("enqueueDownload", async () => { + describe("enqueueDownload", () => { it("should pop download tasks off the queue", async () => { const getResultsSpy = jest.spyOn( variantAnalysisManager, @@ -782,7 +782,7 @@ describe("Variant Analysis Manager", async () => { }); }); - describe("removeVariantAnalysis", async () => { + describe("removeVariantAnalysis", () => { const removeAnalysisResultsStub = jest.spyOn( variantAnalysisResultsManager, "removeAnalysisResults", @@ -814,7 +814,7 @@ describe("Variant Analysis Manager", async () => { }); }); - describe("when rehydrating a query", async () => { + describe("when rehydrating a query", () => { let variantAnalysis: VariantAnalysis; const variantAnalysisRemovedSpy = jest.fn(); const executeCommandSpy = jest.spyOn(commands, "executeCommand"); @@ -830,7 +830,7 @@ describe("Variant Analysis Manager", async () => { executeCommandSpy.mockReset().mockResolvedValue(undefined); }); - describe("when variant analysis record doesn't exist", async () => { + describe("when variant analysis record doesn't exist", () => { it("should remove the variant analysis", async () => { await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); expect(variantAnalysisRemovedSpy).toHaveBeenCalledTimes(1); @@ -842,7 +842,7 @@ describe("Variant Analysis Manager", async () => { }); }); - describe("when variant analysis record does exist", async () => { + describe("when variant analysis record does exist", () => { let variantAnalysisStorageLocation: string; beforeEach(async () => { @@ -857,7 +857,7 @@ describe("Variant Analysis Manager", async () => { fs.rmSync(variantAnalysisStorageLocation, { recursive: true }); }); - describe("when the variant analysis is not complete", async () => { + describe("when the variant analysis is not complete", () => { beforeEach(() => { jest .spyOn(VariantAnalysisModule, "isVariantAnalysisComplete") @@ -882,7 +882,7 @@ describe("Variant Analysis Manager", async () => { }); }); - describe("when the variant analysis is complete", async () => { + describe("when the variant analysis is complete", () => { beforeEach(() => { jest .spyOn(VariantAnalysisModule, "isVariantAnalysisComplete") @@ -906,7 +906,7 @@ describe("Variant Analysis Manager", async () => { }); }); - describe("cancelVariantAnalysis", async () => { + describe("cancelVariantAnalysis", () => { let variantAnalysis: VariantAnalysis; const mockCancelVariantAnalysis = jest.spyOn( ghActionsApiClient, @@ -1005,7 +1005,7 @@ describe("Variant Analysis Manager", async () => { }); }); - describe("copyRepoListToClipboard", async () => { + describe("copyRepoListToClipboard", () => { let variantAnalysis: VariantAnalysis; let variantAnalysisStorageLocation: string; diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts index 03bd39c0a..9d7ff606b 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts @@ -35,7 +35,7 @@ import { VariantAnalysisManager } from "../../../remote-queries/variant-analysis jest.setTimeout(60_000); -describe("Variant Analysis Monitor", async () => { +describe("Variant Analysis Monitor", () => { let extension: CodeQLExtensionInterface | Record; const mockGetVariantAnalysis = jest.spyOn(ghApiClient, "getVariantAnalysis"); let cancellationTokenSource: CancellationTokenSource; @@ -83,7 +83,7 @@ describe("Variant Analysis Monitor", async () => { limitNumberOfAttemptsToMonitor(); }); - describe("when credentials are invalid", async () => { + describe("when credentials are invalid", () => { beforeEach(async () => { jest .spyOn(Credentials, "initialize") @@ -102,7 +102,7 @@ describe("Variant Analysis Monitor", async () => { }); }); - describe("when credentials are valid", async () => { + describe("when credentials are valid", () => { beforeEach(async () => { const mockCredentials = { getOctokit: () => @@ -124,7 +124,7 @@ describe("Variant Analysis Monitor", async () => { expect(result).toEqual({ status: "Canceled" }); }); - describe("when the variant analysis fails", async () => { + describe("when the variant analysis fails", () => { let mockFailedApiResponse: VariantAnalysisApiResponse; beforeEach(async () => { @@ -163,12 +163,12 @@ describe("Variant Analysis Monitor", async () => { }); }); - describe("when the variant analysis is in progress", async () => { + describe("when the variant analysis is in progress", () => { let mockApiResponse: VariantAnalysisApiResponse; let scannedRepos: ApiVariantAnalysisScannedRepository[]; let succeededRepos: ApiVariantAnalysisScannedRepository[]; - describe("when there are successfully scanned repos", async () => { + describe("when there are successfully scanned repos", () => { beforeEach(async () => { scannedRepos = createMockScannedRepos([ "pending", @@ -241,7 +241,7 @@ describe("Variant Analysis Monitor", async () => { }); }); - describe("when there are only in progress repos", async () => { + describe("when there are only in progress repos", () => { let scannedRepos: ApiVariantAnalysisScannedRepository[]; beforeEach(async () => { @@ -270,7 +270,7 @@ describe("Variant Analysis Monitor", async () => { }); }); - describe("when there are no repos to scan", async () => { + describe("when there are no repos to scan", () => { beforeEach(async () => { scannedRepos = []; mockApiResponse = createMockApiResponse("succeeded", scannedRepos); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts index af8c32396..296fff94a 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts @@ -85,7 +85,7 @@ describe(VariantAnalysisResultsManager.name, () => { }); }); - describe("when the artifact_url is missing", async () => { + describe("when the artifact_url is missing", () => { it("should not try to download the result", async () => { const dummyRepoTask = createMockVariantAnalysisRepositoryTask(); delete dummyRepoTask.artifactUrl; @@ -105,7 +105,7 @@ describe(VariantAnalysisResultsManager.name, () => { }); }); - describe("when the artifact_url is present", async () => { + describe("when the artifact_url is present", () => { let arrayBuffer: ArrayBuffer; const getVariantAnalysisRepoResultStub = jest.spyOn( From 4f8d68d8a2428089751669dc43ef03f6352640f8 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 23 Nov 2022 17:16:09 +0100 Subject: [PATCH 15/58] Always activate the extension before running tests --- .../ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts index 35ff08f27..23310aaca 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts @@ -71,6 +71,9 @@ beforeAll(async () => { ); } } + + // Activate the extension + await extensions.getExtension("GitHub.vscode-codeql")?.activate(); }); // ensure extension is cleaned up. From 614785fb7a115d56ccc103b16bff11ba786f9c7a Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 09:07:40 +0100 Subject: [PATCH 16/58] Do not dispose the extension after every test file --- .../vscode-tests/cli-integration/jest.setup.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts index 23310aaca..8b1ef199d 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts @@ -6,7 +6,6 @@ import * as tmp from "tmp"; import { getTestSetting } from "../test-config"; import { CUSTOM_CODEQL_PATH_SETTING } from "../../config"; import { extensions, workspace } from "vscode"; -import { CodeQLExtensionInterface } from "../../extension"; import baseJestSetup from "../jest.setup"; @@ -78,21 +77,6 @@ beforeAll(async () => { // ensure extension is cleaned up. afterAll(async () => { - const extension = await extensions - .getExtension>( - "GitHub.vscode-codeql", - )! - .activate(); - // This shuts down the extension and can only be run after all tests have completed. - // If this is not called, then the test process will hang. - if ("dispose" in extension) { - try { - extension.dispose(); - } catch (e) { - console.warn("Failed to dispose extension", e); - } - } - // ensure temp directory is cleaned up. try { removeStorage?.(); From 1a10fd35f17222502bbc0059ac18999db919e828 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 10:15:47 +0100 Subject: [PATCH 17/58] Remove calls to `fail` Instead of calling `fail`, we can just let the error be caught by Jest, which will automatically fail the tests. For other instances where we're calling `fail` in case an error was not thrown, we will instead use `.rejects.toThrow`. --- .../cli-integration/databases.test.ts | 40 ++--- .../cli-integration/jest.setup.ts | 30 ++-- .../cli-integration/legacy-query.test.ts | 69 ++++---- .../cli-integration/new-query.test.ts | 113 +++++++------ .../cli-integration/queries.test.ts | 149 ++++++++---------- .../remote-queries-manager.test.ts | 7 +- .../variant-analysis-manager.test.ts | 76 ++++----- .../variant-analysis-monitor.test.ts | 16 +- .../variant-analysis-results-manager.test.ts | 36 ++--- ...nt-analysis-submission-integration.test.ts | 14 +- .../cli-integration/run-cli.test.ts | 42 ++--- .../cli-integration/sourcemap.test.ts | 65 ++++---- .../ql-vscode/src/vscode-tests/jest.setup.ts | 7 - 13 files changed, 279 insertions(+), 385 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts index 4e13a524a..7295565ee 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts @@ -29,35 +29,27 @@ describe("Databases", () => { jest.spyOn(window, "showInformationMessage").mockResolvedValue(undefined); beforeEach(async () => { - try { - inputBoxStub.mockReset().mockResolvedValue(undefined); - progressCallback.mockReset(); + inputBoxStub.mockReset().mockResolvedValue(undefined); + progressCallback.mockReset(); - const extension = await extensions - .getExtension>( - "GitHub.vscode-codeql", - )! - .activate(); - if ("databaseManager" in extension) { - databaseManager = extension.databaseManager; - } else { - throw new Error( - "Extension not initialized. Make sure cli is downloaded and installed properly.", - ); - } - - await cleanDatabases(databaseManager); - } catch (e) { - fail(e as Error); + const extension = await extensions + .getExtension>( + "GitHub.vscode-codeql", + )! + .activate(); + if ("databaseManager" in extension) { + databaseManager = extension.databaseManager; + } else { + throw new Error( + "Extension not initialized. Make sure cli is downloaded and installed properly.", + ); } + + await cleanDatabases(databaseManager); }); afterEach(async () => { - try { - await cleanDatabases(databaseManager); - } catch (e) { - fail(e as Error); - } + await cleanDatabases(databaseManager); }); it("should add a database from a folder", async () => { diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts index 8b1ef199d..76af9fdc0 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.setup.ts @@ -26,22 +26,18 @@ beforeAll(async () => { if (!fs.existsSync(dbLoc)) { console.log(`Downloading test database to ${dbLoc}`); - try { - await new Promise((resolve, reject) => { - return fetch(DB_URL).then((response) => { - const dest = fs.createWriteStream(dbLoc); - response.body.pipe(dest); + await new Promise((resolve, reject) => { + return fetch(DB_URL).then((response) => { + const dest = fs.createWriteStream(dbLoc); + response.body.pipe(dest); - response.body.on("error", reject); - dest.on("error", reject); - dest.on("close", () => { - resolve(dbLoc); - }); + response.body.on("error", reject); + dest.on("error", reject); + dest.on("close", () => { + resolve(dbLoc); }); }); - } catch (e) { - fail("Failed to download test database: " + e); - } + }); } // Create the temp directory to be used as extension local storage. @@ -59,14 +55,14 @@ beforeAll(async () => { // check that the codeql folder is found in the workspace const folders = workspace.workspaceFolders; if (!folders) { - fail( - '\n\n\nNo workspace folders found.\nYou will need a local copy of the codeql repo.\nMake sure you specify the path to it in launch.json.\nIt should be something along the lines of "${workspaceRoot}/../codeql" depending on where you have your local copy of the codeql repo.\n\n\n', + throw new Error( + 'No workspace folders found.\nYou will need a local copy of the codeql repo.\nMake sure you specify the path to it in launch.json.\nIt should be something along the lines of "${workspaceRoot}/../codeql" depending on where you have your local copy of the codeql repo.', ); } else { const codeqlFolder = folders.find((folder) => folder.name === "codeql"); if (!codeqlFolder) { - fail( - '\n\n\nNo workspace folders found.\nYou will need a local copy of the codeql repo.\nMake sure you specify the path to it in launch.json.\nIt should be something along the lines of "${workspaceRoot}/../codeql" depending on where you have your local copy of the codeql repo.\n\n\n', + throw new Error( + 'No workspace folders found.\nYou will need a local copy of the codeql repo.\nMake sure you specify the path to it in launch.json.\nIt should be something along the lines of "${workspaceRoot}/../codeql" depending on where you have your local copy of the codeql repo.\n\n\n', ); } } diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts index 6a4753040..b31c81683 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts @@ -9,7 +9,6 @@ import * as cli from "../../cli"; import { CellValue } from "../../pure/bqrs-cli-types"; import { extensions } from "vscode"; import { CodeQLExtensionInterface } from "../../extension"; -import { fail } from "assert"; import { describeWithCodeQL } from "../cli"; import { QueryServerClient } from "../../legacy-query-server/queryserver-client"; import { logger, ProgressReporter } from "../../logging"; @@ -112,43 +111,39 @@ describeWithCodeQL()("using the legacy query server", () => { let cliServer: cli.CodeQLCliServer; beforeAll(async () => { - try { - const extension = await extensions - .getExtension>( - "GitHub.vscode-codeql", - )! - .activate(); - if ("cliServer" in extension) { - cliServer = extension.cliServer; - cliServer.quiet = true; + const extension = await extensions + .getExtension>( + "GitHub.vscode-codeql", + )! + .activate(); + if ("cliServer" in extension) { + cliServer = extension.cliServer; + cliServer.quiet = true; - qs = new QueryServerClient( - { - codeQlPath: - (await extension.distributionManager.getCodeQlPathWithoutVersionCheck()) || - "", - debug: false, - cacheSize: 0, - numThreads: 1, - saveCache: false, - timeoutSecs: 0, - }, - cliServer, - { - contextStoragePath: tmpDir.name, - logger, - }, - (task) => - task(nullProgressReporter, new CancellationTokenSource().token), - ); - await qs.startQueryServer(); - } else { - throw new Error( - "Extension not initialized. Make sure cli is downloaded and installed properly.", - ); - } - } catch (e) { - fail(e as Error); + qs = new QueryServerClient( + { + codeQlPath: + (await extension.distributionManager.getCodeQlPathWithoutVersionCheck()) || + "", + debug: false, + cacheSize: 0, + numThreads: 1, + saveCache: false, + timeoutSecs: 0, + }, + cliServer, + { + contextStoragePath: tmpDir.name, + logger, + }, + (task) => + task(nullProgressReporter, new CancellationTokenSource().token), + ); + await qs.startQueryServer(); + } else { + throw new Error( + "Extension not initialized. Make sure cli is downloaded and installed properly.", + ); } }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts index 6becb807c..b7af27577 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts @@ -7,7 +7,6 @@ import * as cli from "../../cli"; import { CellValue } from "../../pure/bqrs-cli-types"; import { extensions, Uri } from "vscode"; import { CodeQLExtensionInterface } from "../../extension"; -import { fail } from "assert"; import { describeWithCodeQL } from "../cli"; import { QueryServerClient } from "../../query-server/queryserver-client"; import { logger, ProgressReporter } from "../../logging"; @@ -113,67 +112,61 @@ describeWithCodeQL()("using the new query server", () => { let cliServer: cli.CodeQLCliServer; let db: string; beforeAll(async () => { - try { - const extension = await extensions - .getExtension>( - "GitHub.vscode-codeql", - )! - .activate(); - if ("cliServer" in extension && "databaseManager" in extension) { - cliServer = extension.cliServer; + const extension = await extensions + .getExtension>( + "GitHub.vscode-codeql", + )! + .activate(); + if ("cliServer" in extension && "databaseManager" in extension) { + cliServer = extension.cliServer; - cliServer.quiet = true; - if ( - !(await cliServer.cliConstraints.supportsNewQueryServerForTests()) - ) { - testContext.ctx.skip(); - } - qs = new QueryServerClient( - { - codeQlPath: - (await extension.distributionManager.getCodeQlPathWithoutVersionCheck()) || - "", - debug: false, - cacheSize: 0, - numThreads: 1, - saveCache: false, - timeoutSecs: 0, - }, - cliServer, - { - contextStoragePath: tmpDir.name, - logger, - }, - (task) => - task(nullProgressReporter, new CancellationTokenSource().token), - ); - await qs.startQueryServer(); - - // Unlike the old query sevre the new one wants a database and the empty direcrtory is not valid. - // Add a database, but make sure the database manager is empty first - await cleanDatabases(extension.databaseManager); - const uri = Uri.file(dbLoc); - const maybeDbItem = await importArchiveDatabase( - uri.toString(true), - extension.databaseManager, - storagePath, - () => { - /**ignore progress */ - }, - token, - ); - - if (!maybeDbItem) { - throw new Error("Could not import database"); - } - db = maybeDbItem.databaseUri.fsPath; - } else { - throw new Error( - "Extension not initialized. Make sure cli is downloaded and installed properly.", - ); + cliServer.quiet = true; + if (!(await cliServer.cliConstraints.supportsNewQueryServerForTests())) { + testContext.ctx.skip(); } - } catch (e) { - fail(e as Error); + qs = new QueryServerClient( + { + codeQlPath: + (await extension.distributionManager.getCodeQlPathWithoutVersionCheck()) || + "", + debug: false, + cacheSize: 0, + numThreads: 1, + saveCache: false, + timeoutSecs: 0, + }, + cliServer, + { + contextStoragePath: tmpDir.name, + logger, + }, + (task) => + task(nullProgressReporter, new CancellationTokenSource().token), + ); + await qs.startQueryServer(); + + // Unlike the old query sevre the new one wants a database and the empty direcrtory is not valid. + // Add a database, but make sure the database manager is empty first + await cleanDatabases(extension.databaseManager); + const uri = Uri.file(dbLoc); + const maybeDbItem = await importArchiveDatabase( + uri.toString(true), + extension.databaseManager, + storagePath, + () => { + /**ignore progress */ + }, + token, + ); + + if (!maybeDbItem) { + throw new Error("Could not import database"); + } + db = maybeDbItem.databaseUri.fsPath; + } else { + throw new Error( + "Extension not initialized. Make sure cli is downloaded and installed properly.", + ); } }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts index 5a3df451b..84da99b97 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts @@ -1,4 +1,3 @@ -import { fail } from "assert"; import { CancellationToken, commands, @@ -40,103 +39,85 @@ describeWithCodeQL()("Queries", () => { let qlFile: string; beforeEach(async () => { - try { - const extension = await extensions - .getExtension>( - "GitHub.vscode-codeql", - )! - .activate(); - if ("databaseManager" in extension) { - databaseManager = extension.databaseManager; - cli = extension.cliServer; - qs = extension.qs; - cli.quiet = true; - ctx = extension.ctx; - qlpackFile = `${ctx.storageUri?.fsPath}/quick-queries/qlpack.yml`; - qlpackLockFile = `${ctx.storageUri?.fsPath}/quick-queries/codeql-pack.lock.yml`; - oldQlpackLockFile = `${ctx.storageUri?.fsPath}/quick-queries/qlpack.lock.yml`; - qlFile = `${ctx.storageUri?.fsPath}/quick-queries/quick-query.ql`; - } else { - throw new Error( - "Extension not initialized. Make sure cli is downloaded and installed properly.", - ); - } - - // Ensure we are starting from a clean slate. - safeDel(qlFile); - safeDel(qlpackFile); - - progress.mockReset(); - token = {} as CancellationToken; - - // Add a database, but make sure the database manager is empty first - await cleanDatabases(databaseManager); - const uri = Uri.file(dbLoc); - const maybeDbItem = await importArchiveDatabase( - uri.toString(true), - databaseManager, - storagePath, - progress, - token, - cli, + const extension = await extensions + .getExtension>( + "GitHub.vscode-codeql", + )! + .activate(); + if ("databaseManager" in extension) { + databaseManager = extension.databaseManager; + cli = extension.cliServer; + qs = extension.qs; + cli.quiet = true; + ctx = extension.ctx; + qlpackFile = `${ctx.storageUri?.fsPath}/quick-queries/qlpack.yml`; + qlpackLockFile = `${ctx.storageUri?.fsPath}/quick-queries/codeql-pack.lock.yml`; + oldQlpackLockFile = `${ctx.storageUri?.fsPath}/quick-queries/qlpack.lock.yml`; + qlFile = `${ctx.storageUri?.fsPath}/quick-queries/quick-query.ql`; + } else { + throw new Error( + "Extension not initialized. Make sure cli is downloaded and installed properly.", ); - - if (!maybeDbItem) { - throw new Error("Could not import database"); - } - dbItem = maybeDbItem; - } catch (e) { - fail(e as Error); } + + // Ensure we are starting from a clean slate. + safeDel(qlFile); + safeDel(qlpackFile); + + progress.mockReset(); + token = {} as CancellationToken; + + // Add a database, but make sure the database manager is empty first + await cleanDatabases(databaseManager); + const uri = Uri.file(dbLoc); + const maybeDbItem = await importArchiveDatabase( + uri.toString(true), + databaseManager, + storagePath, + progress, + token, + cli, + ); + + if (!maybeDbItem) { + throw new Error("Could not import database"); + } + dbItem = maybeDbItem; }); afterEach(async () => { - try { - safeDel(qlpackFile); - safeDel(qlFile); - await cleanDatabases(databaseManager); - } catch (e) { - fail(e as Error); - } + safeDel(qlpackFile); + safeDel(qlFile); + await cleanDatabases(databaseManager); }); it("should run a query", async () => { - try { - const queryPath = path.join(__dirname, "data", "simple-query.ql"); - const result = qs.compileAndRunQueryAgainstDatabase( - dbItem, - await mockInitialQueryInfo(queryPath), - path.join(tmpDir.name, "mock-storage-path"), - progress, - token, - ); + const queryPath = path.join(__dirname, "data", "simple-query.ql"); + const result = qs.compileAndRunQueryAgainstDatabase( + dbItem, + await mockInitialQueryInfo(queryPath), + path.join(tmpDir.name, "mock-storage-path"), + progress, + token, + ); - // just check that the query was successful - expect((await result).successful).toBe(true); - } catch (e) { - console.error("Test Failed"); - fail(e as Error); - } + // just check that the query was successful + expect((await result).successful).toBe(true); }); // Asserts a fix for bug https://github.com/github/vscode-codeql/issues/733 it("should restart the database and run a query", async () => { - try { - await commands.executeCommand("codeQL.restartQueryServer"); - const queryPath = path.join(__dirname, "data", "simple-query.ql"); - const result = await qs.compileAndRunQueryAgainstDatabase( - dbItem, - await mockInitialQueryInfo(queryPath), - path.join(tmpDir.name, "mock-storage-path"), - progress, - token, - ); + await commands.executeCommand("codeQL.restartQueryServer"); + const queryPath = path.join(__dirname, "data", "simple-query.ql"); + const result = await qs.compileAndRunQueryAgainstDatabase( + dbItem, + await mockInitialQueryInfo(queryPath), + path.join(tmpDir.name, "mock-storage-path"), + progress, + token, + ); - expect(result.successful).toBe(true); - } catch (e) { - console.error("Test Failed"); - fail(e as Error); - } + expect(result.successful).toBe(true); }); it("should create a quick query", async () => { diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts index 157c0a324..c3190e0ff 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts @@ -344,12 +344,7 @@ describe("Remote queries", () => { cancellationTokenSource.cancel(); - try { - await promise; - fail("should have thrown"); - } catch (e) { - expect(e).toBeInstanceOf(UserCancellationException); - } + await expect(promise).rejects.toThrow(UserCancellationException); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts index 89149918d..8a1edd4b4 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts @@ -88,26 +88,22 @@ describe("Variant Analysis Manager", () => { scannedRepos, }); - try { - const extension = await extensions - .getExtension>( - "GitHub.vscode-codeql", - )! - .activate(); - cli = extension.cliServer; - variantAnalysisResultsManager = new VariantAnalysisResultsManager( - cli, - logger, - ); - variantAnalysisManager = new VariantAnalysisManager( - extension.ctx, - cli, - storagePath, - variantAnalysisResultsManager, - ); - } catch (e) { - fail(e as Error); - } + const extension = await extensions + .getExtension>( + "GitHub.vscode-codeql", + )! + .activate(); + cli = extension.cliServer; + variantAnalysisResultsManager = new VariantAnalysisResultsManager( + cli, + logger, + ); + variantAnalysisManager = new VariantAnalysisManager( + extension.ctx, + cli, + storagePath, + variantAnalysisResultsManager, + ); }); describe("runVariantAnalysis", () => { @@ -260,12 +256,7 @@ describe("Variant Analysis Manager", () => { cancellationTokenSource.cancel(); - try { - await promise; - fail("should have thrown"); - } catch (e) { - expect(e).toBeInstanceOf(UserCancellationException); - } + await expect(promise).rejects.toThrow(UserCancellationException); }); }); @@ -566,16 +557,13 @@ describe("Variant Analysis Manager", () => { new Error("Failed to download"), ); - try { - await variantAnalysisManager.autoDownloadVariantAnalysisResult( + await expect( + variantAnalysisManager.autoDownloadVariantAnalysisResult( scannedRepos[0], variantAnalysis, cancellationTokenSource.token, - ); - fail("Expected an error to be thrown"); - } catch (e: any) { - // we can ignore this error, we expect this - } + ), + ).rejects.toThrow(); expect(outputJsonStub).not.toHaveBeenCalled(); }); @@ -585,16 +573,13 @@ describe("Variant Analysis Manager", () => { new Error("Failed to download"), ); - try { - await variantAnalysisManager.autoDownloadVariantAnalysisResult( + await expect( + variantAnalysisManager.autoDownloadVariantAnalysisResult( scannedRepos[0], variantAnalysis, cancellationTokenSource.token, - ); - fail("Expected an error to be thrown"); - } catch (e) { - // we can ignore this error, we expect this - } + ), + ).rejects.toThrow(); expect(outputJsonStub).not.toHaveBeenCalled(); @@ -630,16 +615,13 @@ describe("Variant Analysis Manager", () => { new Error("Failed to download"), ); - try { - await variantAnalysisManager.autoDownloadVariantAnalysisResult( + await expect( + variantAnalysisManager.autoDownloadVariantAnalysisResult( scannedRepos[0], variantAnalysis, cancellationTokenSource.token, - ); - fail("Expected an error to be thrown"); - } catch (e) { - // we can ignore this error, we expect this - } + ), + ).rejects.toThrow(); expect(outputJsonStub).not.toHaveBeenCalled(); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts index 9d7ff606b..addae865d 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts @@ -60,16 +60,12 @@ describe("Variant Analysis Monitor", () => { variantAnalysis = createMockVariantAnalysis({}); - try { - extension = await extensions - .getExtension>( - "GitHub.vscode-codeql", - )! - .activate(); - variantAnalysisMonitor = new VariantAnalysisMonitor(extension.ctx); - } catch (e) { - fail(e as Error); - } + extension = await extensions + .getExtension>( + "GitHub.vscode-codeql", + )! + .activate(); + variantAnalysisMonitor = new VariantAnalysisMonitor(extension.ctx); variantAnalysisManager = extension.variantAnalysisManager; mockGetDownloadResult = jest diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts index 296fff94a..7cb08c3a2 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts @@ -27,20 +27,16 @@ describe(VariantAnalysisResultsManager.name, () => { variantAnalysisId = faker.datatype.number(); - try { - const extension = await extensions - .getExtension>( - "GitHub.vscode-codeql", - )! - .activate(); - cli = extension.cliServer; - variantAnalysisResultsManager = new VariantAnalysisResultsManager( - cli, - logger, - ); - } catch (e) { - fail(e as Error); - } + const extension = await extensions + .getExtension>( + "GitHub.vscode-codeql", + )! + .activate(); + cli = extension.cliServer; + variantAnalysisResultsManager = new VariantAnalysisResultsManager( + cli, + logger, + ); }); describe("download", () => { @@ -90,18 +86,14 @@ describe(VariantAnalysisResultsManager.name, () => { const dummyRepoTask = createMockVariantAnalysisRepositoryTask(); delete dummyRepoTask.artifactUrl; - try { - await variantAnalysisResultsManager.download( + await expect( + variantAnalysisResultsManager.download( mockCredentials, variantAnalysisId, dummyRepoTask, variantAnalysisStoragePath, - ); - - fail("Expected an error to be thrown"); - } catch (e: any) { - expect(e.message).toBe("Missing artifact URL"); - } + ), + ).rejects.toThrow("Missing artifact URL"); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts index 15e2cc4fb..2d87cd117 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts @@ -56,15 +56,11 @@ describe("Variant Analysis Submission Integration", () => { executeCommandSpy.mockRestore(); showErrorMessageSpy.mockReset().mockResolvedValue(undefined); - try { - await extensions - .getExtension>( - "GitHub.vscode-codeql", - )! - .activate(); - } catch (e) { - fail(e as Error); - } + await extensions + .getExtension>( + "GitHub.vscode-codeql", + )! + .activate(); }); describe("Successful scenario", () => { diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts index e913440f4..f28f72286 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts @@ -12,7 +12,6 @@ import { } from "../../helpers"; import { resolveQueries } from "../../contextual/queryResolver"; import { KeyType } from "../../contextual/keyType"; -import { fail } from "assert"; jest.setTimeout(60_000); @@ -88,31 +87,24 @@ describe("Use cli", () => { itWithCodeQL()( "should resolve printAST queries for supported languages", async () => { - try { - for (const lang of supportedLanguages) { - if (lang === "go") { - // The codeql-go submodule is not available in the integration tests. - return; - } - - console.log(`resolving printAST queries for ${lang}`); - const pack = await getQlPackForDbscheme( - cli, - languageToDbScheme[lang], - ); - expect(pack.dbschemePack).toEqual(expect.arrayContaining([lang])); - if (pack.dbschemePackIsLibraryPack) { - expect(pack.queryPack).toEqual(expect.arrayContaining([lang])); - } - - const result = await resolveQueries(cli, pack, KeyType.PrintAstQuery); - - // It doesn't matter what the name or path of the query is, only - // that we have found exactly one query. - expect(result.length).toBe(1); + for (const lang of supportedLanguages) { + if (lang === "go") { + // The codeql-go submodule is not available in the integration tests. + return; } - } catch (e) { - fail(e as Error); + + console.log(`resolving printAST queries for ${lang}`); + const pack = await getQlPackForDbscheme(cli, languageToDbScheme[lang]); + expect(pack.dbschemePack).toEqual(expect.arrayContaining([lang])); + if (pack.dbschemePackIsLibraryPack) { + expect(pack.queryPack).toEqual(expect.arrayContaining([lang])); + } + + const result = await resolveQueries(cli, pack, KeyType.PrintAstQuery); + + // It doesn't matter what the name or path of the query is, only + // that we have found exactly one query. + expect(result.length).toBe(1); } }, ); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/sourcemap.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/sourcemap.test.ts index e174db109..d5d9e643b 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/sourcemap.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/sourcemap.test.ts @@ -1,7 +1,5 @@ -import { fail } from "assert"; import { commands, Selection, window, workspace } from "vscode"; import * as path from "path"; -import * as assert from "assert"; import { tmpDir } from "../../helpers"; import * as fs from "fs-extra"; @@ -12,44 +10,37 @@ jest.setTimeout(20_000); */ describe("SourceMap", () => { it("should jump to QL code", async () => { - try { - const root = workspace.workspaceFolders![0].uri.fsPath; - const srcFiles = { - summary: path.join(root, "log-summary", "evaluator-log.summary"), - summaryMap: path.join(root, "log-summary", "evaluator-log.summary.map"), - }; - // We need to modify the source map so that its paths point to the actual location of the - // workspace root on this machine. We'll copy the summary and its source map to a temp - // directory, modify the source map their, and open that summary. - const tempFiles = await copyFilesToTempDirectory(srcFiles); + const root = workspace.workspaceFolders![0].uri.fsPath; + const srcFiles = { + summary: path.join(root, "log-summary", "evaluator-log.summary"), + summaryMap: path.join(root, "log-summary", "evaluator-log.summary.map"), + }; + // We need to modify the source map so that its paths point to the actual location of the + // workspace root on this machine. We'll copy the summary and its source map to a temp + // directory, modify the source map their, and open that summary. + const tempFiles = await copyFilesToTempDirectory(srcFiles); - // The checked-in sourcemap has placeholders of the form `${root}`, which we need to replace - // with the actual root directory. - const mapText = await fs.readFile(tempFiles.summaryMap, "utf-8"); - // Always use forward slashes, since they work everywhere. - const slashRoot = root.replaceAll("\\", "/"); - const newMapText = mapText.replaceAll("${root}", slashRoot); - await fs.writeFile(tempFiles.summaryMap, newMapText); + // The checked-in sourcemap has placeholders of the form `${root}`, which we need to replace + // with the actual root directory. + const mapText = await fs.readFile(tempFiles.summaryMap, "utf-8"); + // Always use forward slashes, since they work everywhere. + const slashRoot = root.replaceAll("\\", "/"); + const newMapText = mapText.replaceAll("${root}", slashRoot); + await fs.writeFile(tempFiles.summaryMap, newMapText); - const summaryDocument = await workspace.openTextDocument( - tempFiles.summary, - ); - assert(summaryDocument.languageId === "ql-summary"); - const summaryEditor = await window.showTextDocument(summaryDocument); - summaryEditor.selection = new Selection(356, 10, 356, 10); - await commands.executeCommand("codeQL.gotoQL"); + const summaryDocument = await workspace.openTextDocument(tempFiles.summary); + expect(summaryDocument.languageId).toBe("ql-summary"); + const summaryEditor = await window.showTextDocument(summaryDocument); + summaryEditor.selection = new Selection(356, 10, 356, 10); + await commands.executeCommand("codeQL.gotoQL"); - const newEditor = window.activeTextEditor; - expect(newEditor).toBeDefined(); - const newDocument = newEditor!.document; - expect(path.basename(newDocument.fileName)).toBe("Namespace.qll"); - const newSelection = newEditor!.selection; - expect(newSelection.start.line).toBe(60); - expect(newSelection.start.character).toBe(2); - } catch (e) { - console.error("Test Failed"); - fail(e as Error); - } + const newEditor = window.activeTextEditor; + expect(newEditor).toBeDefined(); + const newDocument = newEditor!.document; + expect(path.basename(newDocument.fileName)).toBe("Namespace.qll"); + const newSelection = newEditor!.selection; + expect(newSelection.start.line).toBe(60); + expect(newSelection.start.character).toBe(2); }); async function copyFilesToTempDirectory>( diff --git a/extensions/ql-vscode/src/vscode-tests/jest.setup.ts b/extensions/ql-vscode/src/vscode-tests/jest.setup.ts index 86334151c..a4c1ce25d 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest.setup.ts +++ b/extensions/ql-vscode/src/vscode-tests/jest.setup.ts @@ -5,13 +5,6 @@ import { jestTestConfigHelper } from "./test-config"; /**/ }; -function fail(reason = "fail was called in a test.") { - throw new Error(reason); -} - -// Jest doesn't seem to define this function anymore, but it's in the types, so should be valid. -(global as any).fail = fail; - export default async function setupEnv() { await jestTestConfigHelper(); } From e89507891392aab752a408bbd1ceb5a04e0ef05e Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 10:16:48 +0100 Subject: [PATCH 18/58] Do not use `.skip` from within tests Jest does not support skipping tests when the test has already started (which could also be in a before hook), so we need to manually return from the tests when the CLI version does not support a tested feature. --- .../cli-integration/new-query.test.ts | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts index b7af27577..1071f946c 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts @@ -102,15 +102,12 @@ const nullProgressReporter: ProgressReporter = { jest.setTimeout(20_000); describeWithCodeQL()("using the new query server", () => { - let testContext: any; - - beforeAll(() => { - testContext = {}; - }); - let qs: qsClient.QueryServerClient; let cliServer: cli.CodeQLCliServer; let db: string; + + let supportNewQueryServer = true; + beforeAll(async () => { const extension = await extensions .getExtension>( @@ -122,7 +119,7 @@ describeWithCodeQL()("using the new query server", () => { cliServer.quiet = true; if (!(await cliServer.cliConstraints.supportsNewQueryServerForTests())) { - testContext.ctx.skip(); + supportNewQueryServer = false; } qs = new QueryServerClient( { @@ -176,6 +173,10 @@ describeWithCodeQL()("using the new query server", () => { const parsedResults = new Checkpoint(); it("should register the database", async () => { + if (!supportNewQueryServer) { + return; + } + await qs.sendRequest( messages.registerDatabases, { databases: [db] }, @@ -187,6 +188,10 @@ describeWithCodeQL()("using the new query server", () => { }); it(`should be able to run query ${queryName}`, async () => { + if (!supportNewQueryServer) { + return; + } + try { const params: messages.RunQueryParams = { db, @@ -214,6 +219,10 @@ describeWithCodeQL()("using the new query server", () => { const actualResultSets: ResultSets = {}; it(`should be able to parse results of query ${queryName}`, async () => { + if (!supportNewQueryServer) { + return; + } + await evaluationSucceeded.done(); const info = await cliServer.bqrsInfo(RESULTS_PATH); @@ -228,6 +237,10 @@ describeWithCodeQL()("using the new query server", () => { }); it(`should have correct results for query ${queryName}`, async () => { + if (!supportNewQueryServer) { + return; + } + await parsedResults.done(); expect(actualResultSets!).not.toHaveLength(0); expect(Object.keys(actualResultSets!).sort()).toEqual( From d02a5cd3dc0c44c1e863ce26cf09c0f524f411b1 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 10:18:52 +0100 Subject: [PATCH 19/58] Do not use `toHaveLength` for objects Mocha supported `.to.have.length` for objects, but Jest only allows `toHaveLength` on objects which have a length property (such as arrays). --- .../src/vscode-tests/cli-integration/legacy-query.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts index b31c81683..0161328e3 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts @@ -250,7 +250,7 @@ describeWithCodeQL()("using the legacy query server", () => { it(`should have correct results for query ${queryName}`, async () => { await parsedResults.done(); - expect(actualResultSets!).not.toHaveLength(0); + expect(actualResultSets).not.toEqual({}); expect(Object.keys(actualResultSets!).sort()).toEqual( Object.keys(queryTestCase.expectedResultSets).sort(), ); From 31b64d2f73e6af317b97f0c2185b995784a2e5c8 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 10:20:23 +0100 Subject: [PATCH 20/58] Fix incorrect `to.contain` transformation For `to.contain`, `jest-codemods` seems to have converted these to be `expect.arrayContaining`, even for strings. This will make the correct change for strings. --- .../src/vscode-tests/cli-integration/run-cli.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts index f28f72286..61795dd76 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/run-cli.test.ts @@ -95,9 +95,9 @@ describe("Use cli", () => { console.log(`resolving printAST queries for ${lang}`); const pack = await getQlPackForDbscheme(cli, languageToDbScheme[lang]); - expect(pack.dbschemePack).toEqual(expect.arrayContaining([lang])); + expect(pack.dbschemePack).toContain(lang); if (pack.dbschemePackIsLibraryPack) { - expect(pack.queryPack).toEqual(expect.arrayContaining([lang])); + expect(pack.queryPack).toContain(lang); } const result = await resolveQueries(cli, pack, KeyType.PrintAstQuery); From 558eb19c977b19ab1d1d5125c0ea17d0a6b56aff Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 10:24:48 +0100 Subject: [PATCH 21/58] Remove unnecessary calls to `mockReset` --- .../cli-integration/databases.test.ts | 3 +- .../cli-integration/packaging.test.ts | 9 +++--- .../cli-integration/queries.test.ts | 1 - .../remote-queries-manager.test.ts | 7 ++--- .../variant-analysis-manager.test.ts | 29 +++++++++---------- .../variant-analysis-monitor.test.ts | 4 +-- .../variant-analysis-results-manager.test.ts | 18 +++++------- ...nt-analysis-submission-integration.test.ts | 6 ++-- 8 files changed, 33 insertions(+), 44 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts index 7295565ee..6f676ab04 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts @@ -29,8 +29,7 @@ describe("Databases", () => { jest.spyOn(window, "showInformationMessage").mockResolvedValue(undefined); beforeEach(async () => { - inputBoxStub.mockReset().mockResolvedValue(undefined); - progressCallback.mockReset(); + inputBoxStub.mockResolvedValue(undefined); const extension = await extensions .getExtension>( diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts index 34150a96b..0461940a9 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts @@ -29,11 +29,10 @@ describe("Packaging commands", () => { ); beforeEach(async () => { - progress.mockReset(); - quickPickSpy.mockReset().mockResolvedValue(undefined); - inputBoxSpy.mockReset().mockResolvedValue(undefined); - showAndLogErrorMessageSpy.mockReset().mockResolvedValue(undefined); - showAndLogInformationMessageSpy.mockReset().mockResolvedValue(undefined); + quickPickSpy.mockResolvedValue(undefined); + inputBoxSpy.mockResolvedValue(undefined); + showAndLogErrorMessageSpy.mockResolvedValue(undefined); + showAndLogInformationMessageSpy.mockResolvedValue(undefined); const extension = await extensions .getExtension>( diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts index 84da99b97..2a6402d6f 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts @@ -64,7 +64,6 @@ describeWithCodeQL()("Queries", () => { safeDel(qlFile); safeDel(qlpackFile); - progress.mockReset(); token = {} as CancellationToken; // Add a database, but make sure the database manager is empty first diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts index c3190e0ff..fe282d74a 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts @@ -86,11 +86,8 @@ describe("Remote queries", () => { cancellationTokenSource = new CancellationTokenSource(); - progress.mockReset(); - // Should not have asked for a language showQuickPickSpy - .mockReset() .mockResolvedValueOnce({ repositories: ["github/vscode-codeql"], } as unknown as QuickPickItem) @@ -102,7 +99,7 @@ describe("Remote queries", () => { full_name: "github/vscode-codeql", private: false, }; - getRepositoryFromNwoStub.mockReset().mockResolvedValue(dummyRepository); + getRepositoryFromNwoStub.mockResolvedValue(dummyRepository); // always run in the vscode-codeql repo await setRemoteControllerRepo("github/vscode-codeql"); @@ -137,7 +134,7 @@ describe("Remote queries", () => { const executeCommandSpy = jest.spyOn(commands, "executeCommand"); beforeEach(() => { - mockSubmitRemoteQueries.mockReset().mockResolvedValue({ + mockSubmitRemoteQueries.mockResolvedValue({ workflow_run_id: 20, repositories_queried: ["octodemo/hello-world-1"], }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts index 8a1edd4b4..997e3d331 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts @@ -136,10 +136,9 @@ describe("Variant Analysis Manager", () => { beforeEach(async () => { writeFileStub.mockRestore(); - progress.mockReset(); // Should not have asked for a language showQuickPickSpy - .mockReset() + .mockResolvedValueOnce({ repositories: ["github/vscode-codeql"], } as unknown as QuickPickItem) @@ -155,10 +154,10 @@ describe("Variant Analysis Manager", () => { full_name: "github/vscode-codeql", private: false, }; - mockGetRepositoryFromNwo.mockReset().mockResolvedValue(dummyRepository); + mockGetRepositoryFromNwo.mockResolvedValue(dummyRepository); mockApiResponse = createMockApiResponse("in_progress"); - mockSubmitVariantAnalysis.mockReset().mockResolvedValue(mockApiResponse); + mockSubmitVariantAnalysis.mockResolvedValue(mockApiResponse); // always run in the vscode-codeql repo await setRemoteControllerRepo("github/vscode-codeql"); @@ -266,7 +265,7 @@ describe("Variant Analysis Manager", () => { describe("when the directory does not exist", () => { beforeEach(() => { const originalFs = jest.requireActual("fs-extras"); - pathExistsStub.mockReset().mockImplementation((...args) => { + pathExistsStub.mockImplementation((...args) => { if ( args[0] === path.join(storagePath, variantAnalysis.id.toString()) ) { @@ -292,7 +291,7 @@ describe("Variant Analysis Manager", () => { describe("when the directory exists", () => { beforeEach(() => { const originalFs = jest.requireActual("fs-extras"); - pathExistsStub.mockReset().mockImplementation((...args) => { + pathExistsStub.mockImplementation((...args) => { if ( args[0] === path.join(storagePath, variantAnalysis.id.toString()) ) { @@ -443,8 +442,8 @@ describe("Variant Analysis Manager", () => { ); arrayBuffer = fs.readFileSync(sourceFilePath).buffer; - getVariantAnalysisRepoStub.mockReset(); - getVariantAnalysisRepoResultStub.mockReset(); + getVariantAnalysisRepoStub; + getVariantAnalysisRepoResultStub; }); describe("when the artifact_url is missing", () => { @@ -658,7 +657,7 @@ describe("Variant Analysis Manager", () => { // the methods are called. const originalFs = jest.requireActual("fs-extras"); - pathExistsStub.mockReset().mockImplementation((...args) => { + pathExistsStub.mockImplementation((...args) => { if ( args[0] === path.join(storagePath, variantAnalysis.id.toString()) ) { @@ -774,8 +773,8 @@ describe("Variant Analysis Manager", () => { beforeEach(async () => { dummyVariantAnalysis = createMockVariantAnalysis({}); - removeAnalysisResultsStub.mockReset().mockReturnValue(undefined); - removeStorageStub.mockReset().mockReturnValue(undefined); + removeAnalysisResultsStub.mockReturnValue(undefined); + removeStorageStub.mockReturnValue(undefined); }); it("should remove variant analysis", async () => { @@ -804,12 +803,12 @@ describe("Variant Analysis Manager", () => { beforeEach(() => { variantAnalysis = createMockVariantAnalysis({}); - variantAnalysisRemovedSpy.mockReset(); + variantAnalysisRemovedSpy; variantAnalysisManager.onVariantAnalysisRemoved( variantAnalysisRemovedSpy, ); - executeCommandSpy.mockReset().mockResolvedValue(undefined); + executeCommandSpy.mockResolvedValue(undefined); }); describe("when variant analysis record doesn't exist", () => { @@ -900,7 +899,7 @@ describe("Variant Analysis Manager", () => { beforeEach(async () => { variantAnalysis = createMockVariantAnalysis({}); - mockCancelVariantAnalysis.mockReset().mockResolvedValue(undefined); + mockCancelVariantAnalysis.mockResolvedValue(undefined); variantAnalysisStorageLocation = variantAnalysisManager.getVariantAnalysisStorageLocation( @@ -1003,7 +1002,7 @@ describe("Variant Analysis Manager", () => { await createTimestampFile(variantAnalysisStorageLocation); await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); - writeTextStub.mockReset(); + writeTextStub; jest.spyOn(env, "clipboard", "get").mockReturnValue({ readText: jest.fn(), writeText: writeTextStub, diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts index addae865d..37b66000e 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts @@ -72,9 +72,7 @@ describe("Variant Analysis Monitor", () => { .spyOn(variantAnalysisManager, "autoDownloadVariantAnalysisResult") .mockResolvedValue(undefined); - mockGetVariantAnalysis - .mockReset() - .mockRejectedValue(new Error("Not mocked")); + mockGetVariantAnalysis.mockRejectedValue(new Error("Not mocked")); limitNumberOfAttemptsToMonitor(); }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts index 7cb08c3a2..c13f23533 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts @@ -112,16 +112,14 @@ describe(VariantAnalysisResultsManager.name, () => { ); arrayBuffer = fs.readFileSync(sourceFilePath).buffer; - getVariantAnalysisRepoResultStub - .mockReset() - .mockImplementation( - (_credentials: Credentials, downloadUrl: string) => { - if (downloadUrl === dummyRepoTask.artifactUrl) { - return Promise.resolve(arrayBuffer); - } - return Promise.reject(new Error("Unexpected artifact URL")); - }, - ); + getVariantAnalysisRepoResultStub.mockImplementation( + (_credentials: Credentials, downloadUrl: string) => { + if (downloadUrl === dummyRepoTask.artifactUrl) { + return Promise.resolve(arrayBuffer); + } + return Promise.reject(new Error("Unexpected artifact URL")); + }, + ); }); it("should call the API to download the results", async () => { diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts index 2d87cd117..af9cf5d32 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts @@ -50,11 +50,11 @@ describe("Variant Analysis Submission Integration", () => { await config.setRemoteControllerRepo("github/vscode-codeql"); - quickPickSpy.mockReset().mockResolvedValue(undefined); - inputBoxSpy.mockReset().mockResolvedValue(undefined); + quickPickSpy.mockResolvedValue(undefined); + inputBoxSpy.mockResolvedValue(undefined); executeCommandSpy.mockRestore(); - showErrorMessageSpy.mockReset().mockResolvedValue(undefined); + showErrorMessageSpy.mockResolvedValue(undefined); await extensions .getExtension>( From e04f941cee63d96ccbce7f47b0ae2fb9016ef362 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 10:44:08 +0100 Subject: [PATCH 22/58] Fix spy on undefined variable --- extensions/ql-vscode/package.json | 2 +- .../variant-analysis-manager.test.ts | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 84183eecd..a80973645 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1275,7 +1275,7 @@ "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", - "cli-integration": "jest --config=out/vscode-tests/cli-integration/jest.config.js out/vscode-tests/cli-integration", + "cli-integration": "jest --projects out/vscode-tests/cli-integration", "update-vscode": "node ./node_modules/vscode/bin/install", "format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix", "lint": "eslint . --ext .ts,.tsx --max-warnings=0", diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts index 997e3d331..3016b63d4 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts @@ -764,16 +764,20 @@ describe("Variant Analysis Manager", () => { }); describe("removeVariantAnalysis", () => { - const removeAnalysisResultsStub = jest.spyOn( - variantAnalysisResultsManager, - "removeAnalysisResults", - ); + let removeAnalysisResultsStub: jest.SpyInstance< + void, + [variantAnalysis: VariantAnalysisModule.VariantAnalysis] + >; const removeStorageStub = jest.spyOn(fs, "remove"); let dummyVariantAnalysis: VariantAnalysis; beforeEach(async () => { dummyVariantAnalysis = createMockVariantAnalysis({}); - removeAnalysisResultsStub.mockReturnValue(undefined); + + removeAnalysisResultsStub = jest + .spyOn(variantAnalysisResultsManager, "removeAnalysisResults") + .mockReturnValue(undefined); + removeStorageStub.mockReturnValue(undefined); }); From 68d867ef763996d25accb348c9e306babadcbf0d Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 11:36:03 +0100 Subject: [PATCH 23/58] Fix remote queries manager tests There were some things that were breaking due to version checks. Since we aren't testing on these CLI versions (2.7.2 and 2.7.4 or older) anymore, we can remove these checks and simplify the tests. --- .../remote-queries-manager.test.ts | 49 ++----------------- 1 file changed, 5 insertions(+), 44 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts index fe282d74a..c752bfb74 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts @@ -8,7 +8,6 @@ import { Uri, window, } from "vscode"; -import * as os from "os"; import * as yaml from "js-yaml"; import { QlPack } from "../../../remote-queries/run-remote-query"; @@ -20,7 +19,6 @@ import { } from "../../../config"; import { UserCancellationException } from "../../../commandRunner"; import * as ghApiClient from "../../../remote-queries/gh-api/gh-api-client"; -import { lte } from "semver"; import { Repository } from "../../../remote-queries/gh-api/repository"; import { createMockExtensionContext } from "../../no-workspace"; import { OutputChannelLogger } from "../../../logging"; @@ -138,8 +136,6 @@ describe("Remote queries", () => { workflow_run_id: 20, repositories_queried: ["octodemo/hello-world-1"], }); - - executeCommandSpy.mockRestore(); }); it("should run a remote query that is part of a qlpack", async () => { @@ -186,21 +182,13 @@ describe("Remote queries", () => { ); expect(qlpackContents.name).toBe("codeql-remote/query"); - verifyQlPack( - "in-pack.ql", - packFS.fileContents("qlpack.yml"), - "0.0.0", - await pathSerializationBroken(), - ); + verifyQlPack("in-pack.ql", packFS.fileContents("qlpack.yml"), "0.0.0"); const libraryDir = ".codeql/libraries/codeql"; const packNames = packFS.directoryContents(libraryDir).sort(); // check dependencies. - // 2.7.4 and earlier have ['javascript-all', 'javascript-upgrades'] - // later only have ['javascript-all']. ensure this test can handle either - expect(packNames.length).to.be.lessThan(3).toBeGreaterThan(0); - expect(packNames[0]).toEqual("javascript-all"); + expect(packNames).toEqual(["javascript-all"]); }); it("should run a remote query that is not part of a qlpack", async () => { @@ -242,12 +230,7 @@ describe("Remote queries", () => { expect(packFS.fileExists("not-in-pack.ql")).toBe(false); // the compiled pack - verifyQlPack( - "in-pack.ql", - packFS.fileContents("qlpack.yml"), - "0.0.0", - await pathSerializationBroken(), - ); + verifyQlPack("in-pack.ql", packFS.fileContents("qlpack.yml"), "0.0.0"); // should have generated a correct qlpack file const qlpackContents: any = yaml.load( @@ -261,10 +244,7 @@ describe("Remote queries", () => { const packNames = packFS.directoryContents(libraryDir).sort(); // check dependencies. - // 2.7.4 and earlier have ['javascript-all', 'javascript-upgrades'] - // later only have ['javascript-all']. ensure this test can handle either - expect(packNames.length).to.be.lessThan(3).toBeGreaterThan(0); - expect(packNames[0]).toEqual("javascript-all"); + expect(packNames).toEqual(["javascript-all"]); }); it("should run a remote query that is nested inside a qlpack", async () => { @@ -309,7 +289,6 @@ describe("Remote queries", () => { "subfolder/in-pack.ql", packFS.fileContents("qlpack.yml"), "0.0.0", - await pathSerializationBroken(), ); // should have generated a correct qlpack file @@ -324,10 +303,7 @@ describe("Remote queries", () => { const packNames = packFS.directoryContents(libraryDir).sort(); // check dependencies. - // 2.7.4 and earlier have ['javascript-all', 'javascript-upgrades'] - // later only have ['javascript-all']. ensure this test can handle either - expect(packNames.length).to.be.lessThan(3).toBeGreaterThan(0); - expect(packNames[0]).toEqual("javascript-all"); + expect(packNames).toEqual(["javascript-all"]); }); it("should cancel a run before uploading", async () => { @@ -349,15 +325,9 @@ describe("Remote queries", () => { queryPath: string, contents: Buffer, packVersion: string, - pathSerializationBroken: boolean, ) { const qlPack = yaml.load(contents.toString("utf-8")) as QlPack; - if (pathSerializationBroken) { - // the path serialization is broken, so we force it to be the path in the pack to be same as the query path - qlPack.defaultSuite![1].query = queryPath; - } - // don't check the build metadata since it is variable delete (qlPack as any).buildMetadata; @@ -379,15 +349,6 @@ describe("Remote queries", () => { }); } - /** - * In version 2.7.2 and earlier, relative paths were not serialized correctly inside the qlpack.yml file. - * So, ignore part of the test for these versions. - * - * @returns true if path serialization is broken in this run - */ - async function pathSerializationBroken() { - return lte(await cli.getVersion(), "2.7.2") && os.platform() === "win32"; - } function getFile(file: string): Uri { return Uri.file(path.join(baseDir, file)); } From 1c0d6b991af6b1f9c3e7405bd12b72abcf37f7d4 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 12:27:18 +0100 Subject: [PATCH 24/58] Enable `restoreMocks` Jest configuration option This will ensure all mocks are restored after every test. This required a significant amount of changes in the tests since `jest.spyOn` now needs to be called in `beforeEach`, rather than in the `describe` block. --- .../cli-integration/databases.test.ts | 12 +- .../cli-integration/packaging.test.ts | 34 ++- .../remote-queries-manager.test.ts | 33 ++- .../variant-analysis-manager.test.ts | 244 ++++++++---------- .../variant-analysis-monitor.test.ts | 25 +- .../variant-analysis-results-manager.test.ts | 25 +- ...nt-analysis-submission-integration.test.ts | 23 +- .../src/vscode-tests/jest.config.base.ts | 2 +- .../contextual/queryResolver.test.ts | 34 ++- .../no-workspace/databaseFetcher.test.ts | 13 +- .../no-workspace/distribution.test.ts | 30 ++- .../vscode-tests/no-workspace/helpers.test.ts | 11 +- .../no-workspace/query-history.test.ts | 53 ++-- .../remote-query-history.test.ts | 14 +- .../repository-selection.test.ts | 52 ++-- .../variant-analysis-history.test.ts | 14 +- .../no-workspace/run-queries.test.ts | 4 +- .../no-workspace/telemetry.test.ts | 41 +-- 18 files changed, 352 insertions(+), 312 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts index 6f676ab04..1ce936dd2 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts @@ -21,15 +21,17 @@ describe("Databases", () => { "https://lgtm.com/projects/g/aeisenberg/angular-bind-notifier/"; let databaseManager: DatabaseManager; - const inputBoxStub = jest.spyOn(window, "showInputBox"); + let inputBoxStub: jest.SpiedFunction; let cli: CodeQLCliServer; const progressCallback = jest.fn(); - jest.spyOn(window, "showErrorMessage").mockResolvedValue(undefined); - jest.spyOn(window, "showInformationMessage").mockResolvedValue(undefined); - beforeEach(async () => { - inputBoxStub.mockResolvedValue(undefined); + inputBoxStub = jest + .spyOn(window, "showInputBox") + .mockResolvedValue(undefined); + + jest.spyOn(window, "showErrorMessage").mockResolvedValue(undefined); + jest.spyOn(window, "showInformationMessage").mockResolvedValue(undefined); const extension = await extensions .getExtension>( diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts index 0461940a9..8c2892deb 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/packaging.test.ts @@ -17,22 +17,28 @@ jest.setTimeout(3 * 60 * 1000); describe("Packaging commands", () => { let cli: CodeQLCliServer; const progress = jest.fn(); - const quickPickSpy = jest.spyOn(window, "showQuickPick"); - const inputBoxSpy = jest.spyOn(window, "showInputBox"); - const showAndLogErrorMessageSpy = jest.spyOn( - helpers, - "showAndLogErrorMessage", - ); - const showAndLogInformationMessageSpy = jest.spyOn( - helpers, - "showAndLogInformationMessage", - ); + let quickPickSpy: jest.SpiedFunction; + let inputBoxSpy: jest.SpiedFunction; + let showAndLogErrorMessageSpy: jest.SpiedFunction< + typeof helpers.showAndLogErrorMessage + >; + let showAndLogInformationMessageSpy: jest.SpiedFunction< + typeof helpers.showAndLogInformationMessage + >; beforeEach(async () => { - quickPickSpy.mockResolvedValue(undefined); - inputBoxSpy.mockResolvedValue(undefined); - showAndLogErrorMessageSpy.mockResolvedValue(undefined); - showAndLogInformationMessageSpy.mockResolvedValue(undefined); + quickPickSpy = jest + .spyOn(window, "showQuickPick") + .mockResolvedValue(undefined); + inputBoxSpy = jest + .spyOn(window, "showInputBox") + .mockResolvedValue(undefined); + showAndLogErrorMessageSpy = jest + .spyOn(helpers, "showAndLogErrorMessage") + .mockResolvedValue(undefined); + showAndLogInformationMessageSpy = jest + .spyOn(helpers, "showAndLogInformationMessage") + .mockResolvedValue(undefined); const extension = await extensions .getExtension>( diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts index c752bfb74..199e76005 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts @@ -47,11 +47,10 @@ describe("Remote queries", () => { let cli: CodeQLCliServer; let cancellationTokenSource: CancellationTokenSource; const progress = jest.fn(); - const showQuickPickSpy = jest.spyOn(window, "showQuickPick"); - const getRepositoryFromNwoStub = jest.spyOn( - ghApiClient, - "getRepositoryFromNwo", - ); + let showQuickPickSpy: jest.SpiedFunction; + let getRepositoryFromNwoStub: jest.SpiedFunction< + typeof ghApiClient.getRepositoryFromNwo + >; let ctx: ExtensionContext; let logger: any; let remoteQueriesManager: RemoteQueriesManager; @@ -59,6 +58,9 @@ describe("Remote queries", () => { let originalDeps: Record | undefined; beforeEach(async () => { + showQuickPickSpy = jest.spyOn(window, "showQuickPick"); + getRepositoryFromNwoStub = jest.spyOn(ghApiClient, "getRepositoryFromNwo"); + const extension = await extensions .getExtension>( "GitHub.vscode-codeql", @@ -125,17 +127,20 @@ describe("Remote queries", () => { }); describe("runRemoteQuery", () => { - const mockSubmitRemoteQueries = jest.spyOn( - ghApiClient, - "submitRemoteQueries", - ); - const executeCommandSpy = jest.spyOn(commands, "executeCommand"); + let mockSubmitRemoteQueries: jest.SpiedFunction< + typeof ghApiClient.submitRemoteQueries + >; + let executeCommandSpy: jest.SpiedFunction; beforeEach(() => { - mockSubmitRemoteQueries.mockResolvedValue({ - workflow_run_id: 20, - repositories_queried: ["octodemo/hello-world-1"], - }); + mockSubmitRemoteQueries = jest + .spyOn(ghApiClient, "submitRemoteQueries") + .mockResolvedValue({ + workflow_run_id: 20, + repositories_queried: ["octodemo/hello-world-1"], + }); + + executeCommandSpy = jest.spyOn(commands, "executeCommand"); }); it("should run a remote query that is part of a qlpack", async () => { diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts index 3016b63d4..4b6aa294d 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts @@ -58,10 +58,9 @@ import { jest.setTimeout(3 * 60 * 1000); describe("Variant Analysis Manager", () => { - const pathExistsStub = jest.spyOn(fs, "pathExists"); - const readJsonStub = jest.spyOn(fs, "readJson"); - const outputJsonStub = jest.spyOn(fs, "outputJson"); - const writeFileStub = jest.spyOn(fs, "writeFile"); + let pathExistsStub: jest.SpiedFunction; + let readJsonStub: jest.SpiedFunction; + let outputJsonStub: jest.SpiedFunction; let cli: CodeQLCliServer; let cancellationTokenSource: CancellationTokenSource; let variantAnalysisManager: VariantAnalysisManager; @@ -70,15 +69,16 @@ describe("Variant Analysis Manager", () => { let scannedRepos: VariantAnalysisScannedRepository[]; beforeEach(async () => { + pathExistsStub = jest.spyOn(fs, "pathExists"); + readJsonStub = jest.spyOn(fs, "readJson"); + outputJsonStub = jest.spyOn(fs, "outputJson").mockReturnValue(undefined); + jest.spyOn(logger, "log").mockResolvedValue(undefined); jest .spyOn(config, "isVariantAnalysisLiveResultsEnabled") .mockReturnValue(false); jest.spyOn(fs, "mkdirSync").mockReturnValue(undefined); - writeFileStub.mockReturnValue(undefined); - pathExistsStub.mockRestore(); - readJsonStub.mockRestore(); - outputJsonStub.mockReturnValue(undefined); + jest.spyOn(fs, "writeFile").mockReturnValue(undefined); cancellationTokenSource = new CancellationTokenSource(); @@ -108,18 +108,16 @@ describe("Variant Analysis Manager", () => { describe("runVariantAnalysis", () => { const progress = jest.fn(); - const showQuickPickSpy = jest.spyOn(window, "showQuickPick"); - const mockGetRepositoryFromNwo = jest.spyOn( - ghApiClient, - "getRepositoryFromNwo", - ); - const mockSubmitVariantAnalysis = jest.spyOn( - ghApiClient, - "submitVariantAnalysis", - ); + let showQuickPickSpy: jest.SpiedFunction; + let mockGetRepositoryFromNwo: jest.SpiedFunction< + typeof ghApiClient.getRepositoryFromNwo + >; + let mockSubmitVariantAnalysis: jest.SpiedFunction< + typeof ghApiClient.submitVariantAnalysis + >; let mockApiResponse: VariantAnalysisApiResponse; let originalDeps: Record | undefined; - const executeCommandSpy = jest.spyOn(commands, "executeCommand"); + let executeCommandSpy: jest.SpiedFunction; const baseDir = path.join( __dirname, @@ -134,18 +132,14 @@ describe("Variant Analysis Manager", () => { } beforeEach(async () => { - writeFileStub.mockRestore(); - // Should not have asked for a language - showQuickPickSpy - + showQuickPickSpy = jest + .spyOn(window, "showQuickPick") .mockResolvedValueOnce({ repositories: ["github/vscode-codeql"], } as unknown as QuickPickItem) .mockResolvedValueOnce("javascript" as unknown as QuickPickItem); - executeCommandSpy.mockRestore(); - cancellationTokenSource = new CancellationTokenSource(); const dummyRepository: Repository = { @@ -154,10 +148,16 @@ describe("Variant Analysis Manager", () => { full_name: "github/vscode-codeql", private: false, }; - mockGetRepositoryFromNwo.mockResolvedValue(dummyRepository); + mockGetRepositoryFromNwo = jest + .spyOn(ghApiClient, "getRepositoryFromNwo") + .mockResolvedValue(dummyRepository); mockApiResponse = createMockApiResponse("in_progress"); - mockSubmitVariantAnalysis.mockResolvedValue(mockApiResponse); + mockSubmitVariantAnalysis = jest + .spyOn(ghApiClient, "submitVariantAnalysis") + .mockResolvedValue(mockApiResponse); + + executeCommandSpy = jest.spyOn(commands, "executeCommand"); // always run in the vscode-codeql repo await setRemoteControllerRepo("github/vscode-codeql"); @@ -264,15 +264,7 @@ describe("Variant Analysis Manager", () => { describe("when the directory does not exist", () => { beforeEach(() => { - const originalFs = jest.requireActual("fs-extras"); - pathExistsStub.mockImplementation((...args) => { - if ( - args[0] === path.join(storagePath, variantAnalysis.id.toString()) - ) { - return false; - } - return originalFs.pathExists(...args); - }); + pathExistsStub.mockImplementation(() => false); }); it("should fire the removed event if the file does not exist", async () => { @@ -282,6 +274,7 @@ describe("Variant Analysis Manager", () => { await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); expect(stub).toBeCalledTimes(1); + expect(pathExistsStub).toHaveBeenCalledTimes(1); expect(pathExistsStub).toBeCalledWith( path.join(storagePath, variantAnalysis.id.toString()), ); @@ -290,15 +283,7 @@ describe("Variant Analysis Manager", () => { describe("when the directory exists", () => { beforeEach(() => { - const originalFs = jest.requireActual("fs-extras"); - pathExistsStub.mockImplementation((...args) => { - if ( - args[0] === path.join(storagePath, variantAnalysis.id.toString()) - ) { - return true; - } - return originalFs.pathExists(...args); - }); + pathExistsStub.mockImplementation(() => true); }); it("should store the variant analysis", async () => { @@ -307,26 +292,21 @@ describe("Variant Analysis Manager", () => { expect( await variantAnalysisManager.getVariantAnalysis(variantAnalysis.id), ).toEqual(variantAnalysis); + + expect(pathExistsStub).toHaveBeenCalledTimes(1); + expect(pathExistsStub).toBeCalledWith( + path.join(storagePath, variantAnalysis.id.toString()), + ); }); it("should not error if the repo states file does not exist", async () => { - const originalFs = jest.requireActual("fs-extras"); - readJsonStub.mockImplementation((...args) => { - if ( - args[0] === - path.join( - storagePath, - variantAnalysis.id.toString(), - "repo_states.json", - ) - ) { - return Promise.reject(new Error("File does not exist")); - } - return originalFs.readJson(...args); - }); + readJsonStub.mockImplementation(() => + Promise.reject(new Error("File does not exist")), + ); await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); + expect(readJsonStub).toHaveBeenCalledTimes(1); expect(readJsonStub).toHaveBeenCalledWith( path.join( storagePath, @@ -340,34 +320,24 @@ describe("Variant Analysis Manager", () => { }); it("should read in the repo states if it exists", async () => { - const originalFs = jest.requireActual("fs-extras"); - readJsonStub.mockImplementation((...args) => { - if ( - args[0] === - path.join( - storagePath, - variantAnalysis.id.toString(), - "repo_states.json", - ) - ) { - return Promise.resolve({ - [scannedRepos[0].repository.id]: { - repositoryId: scannedRepos[0].repository.id, - downloadStatus: - VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, - }, - [scannedRepos[1].repository.id]: { - repositoryId: scannedRepos[1].repository.id, - downloadStatus: - VariantAnalysisScannedRepositoryDownloadStatus.InProgress, - }, - }); - } - return originalFs.readJson(...args); - }); + readJsonStub.mockImplementation(() => + Promise.resolve({ + [scannedRepos[0].repository.id]: { + repositoryId: scannedRepos[0].repository.id, + downloadStatus: + VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, + }, + [scannedRepos[1].repository.id]: { + repositoryId: scannedRepos[1].repository.id, + downloadStatus: + VariantAnalysisScannedRepositoryDownloadStatus.InProgress, + }, + }), + ); await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); + expect(readJsonStub).toHaveBeenCalledTimes(1); expect(readJsonStub).toHaveBeenCalledWith( path.join( storagePath, @@ -418,14 +388,12 @@ describe("Variant Analysis Manager", () => { describe("when credentials are valid", () => { let arrayBuffer: ArrayBuffer; - const getVariantAnalysisRepoStub = jest.spyOn( - ghApiClient, - "getVariantAnalysisRepo", - ); - const getVariantAnalysisRepoResultStub = jest.spyOn( - ghApiClient, - "getVariantAnalysisRepoResult", - ); + let getVariantAnalysisRepoStub: jest.SpiedFunction< + typeof ghApiClient.getVariantAnalysisRepo + >; + let getVariantAnalysisRepoResultStub: jest.SpiedFunction< + typeof ghApiClient.getVariantAnalysisRepoResult + >; beforeEach(async () => { const mockCredentials = { @@ -442,8 +410,14 @@ describe("Variant Analysis Manager", () => { ); arrayBuffer = fs.readFileSync(sourceFilePath).buffer; - getVariantAnalysisRepoStub; - getVariantAnalysisRepoResultStub; + getVariantAnalysisRepoStub = jest.spyOn( + ghApiClient, + "getVariantAnalysisRepo", + ); + getVariantAnalysisRepoResultStub = jest.spyOn( + ghApiClient, + "getVariantAnalysisRepoResult", + ); }); describe("when the artifact_url is missing", () => { @@ -656,44 +630,32 @@ describe("Variant Analysis Manager", () => { // The actual tests for these are in rehydrateVariantAnalysis, so we can just mock them here and test that // the methods are called. - const originalFs = jest.requireActual("fs-extras"); - pathExistsStub.mockImplementation((...args) => { - if ( - args[0] === path.join(storagePath, variantAnalysis.id.toString()) - ) { - return false; - } - return originalFs.pathExists(...args); - }); + pathExistsStub.mockImplementation(() => false); // This will read in the correct repo states - readJsonStub.mockImplementation((...args) => { - if ( - args[0] === - path.join( - storagePath, - variantAnalysis.id.toString(), - "repo_states.json", - ) - ) { - return Promise.resolve({ - [scannedRepos[1].repository.id]: { - repositoryId: scannedRepos[1].repository.id, - downloadStatus: - VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, - }, - [scannedRepos[2].repository.id]: { - repositoryId: scannedRepos[2].repository.id, - downloadStatus: - VariantAnalysisScannedRepositoryDownloadStatus.InProgress, - }, - }); - } - return originalFs.readJson(...args); - }); + readJsonStub.mockImplementation(() => + Promise.resolve({ + [scannedRepos[1].repository.id]: { + repositoryId: scannedRepos[1].repository.id, + downloadStatus: + VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, + }, + [scannedRepos[2].repository.id]: { + repositoryId: scannedRepos[2].repository.id, + downloadStatus: + VariantAnalysisScannedRepositoryDownloadStatus.InProgress, + }, + }), + ); await variantAnalysisManager.rehydrateVariantAnalysis( variantAnalysis, ); + + expect(pathExistsStub).toHaveBeenCalledTimes(1); + expect(pathExistsStub).toBeCalledWith( + path.join(storagePath, variantAnalysis.id.toString()), + ); + expect(readJsonStub).toHaveBeenCalledTimes(1); expect(readJsonStub).toHaveBeenCalledWith( path.join( storagePath, @@ -764,11 +726,10 @@ describe("Variant Analysis Manager", () => { }); describe("removeVariantAnalysis", () => { - let removeAnalysisResultsStub: jest.SpyInstance< - void, - [variantAnalysis: VariantAnalysisModule.VariantAnalysis] + let removeAnalysisResultsStub: jest.SpiedFunction< + typeof variantAnalysisResultsManager.removeAnalysisResults >; - const removeStorageStub = jest.spyOn(fs, "remove"); + let removeStorageStub: jest.SpiedFunction; let dummyVariantAnalysis: VariantAnalysis; beforeEach(async () => { @@ -778,7 +739,9 @@ describe("Variant Analysis Manager", () => { .spyOn(variantAnalysisResultsManager, "removeAnalysisResults") .mockReturnValue(undefined); - removeStorageStub.mockReturnValue(undefined); + removeStorageStub = jest + .spyOn(fs, "remove") + .mockReturnValue(undefined); }); it("should remove variant analysis", async () => { @@ -802,17 +765,18 @@ describe("Variant Analysis Manager", () => { describe("when rehydrating a query", () => { let variantAnalysis: VariantAnalysis; const variantAnalysisRemovedSpy = jest.fn(); - const executeCommandSpy = jest.spyOn(commands, "executeCommand"); + let executeCommandSpy: jest.SpiedFunction; beforeEach(() => { variantAnalysis = createMockVariantAnalysis({}); - variantAnalysisRemovedSpy; variantAnalysisManager.onVariantAnalysisRemoved( variantAnalysisRemovedSpy, ); - executeCommandSpy.mockResolvedValue(undefined); + executeCommandSpy = jest + .spyOn(commands, "executeCommand") + .mockResolvedValue(undefined); }); describe("when variant analysis record doesn't exist", () => { @@ -893,17 +857,18 @@ describe("Variant Analysis Manager", () => { describe("cancelVariantAnalysis", () => { let variantAnalysis: VariantAnalysis; - const mockCancelVariantAnalysis = jest.spyOn( - ghActionsApiClient, - "cancelVariantAnalysis", - ); + let mockCancelVariantAnalysis: jest.SpiedFunction< + typeof ghActionsApiClient.cancelVariantAnalysis + >; let variantAnalysisStorageLocation: string; beforeEach(async () => { variantAnalysis = createMockVariantAnalysis({}); - mockCancelVariantAnalysis.mockResolvedValue(undefined); + mockCancelVariantAnalysis = jest + .spyOn(ghActionsApiClient, "cancelVariantAnalysis") + .mockResolvedValue(undefined); variantAnalysisStorageLocation = variantAnalysisManager.getVariantAnalysisStorageLocation( @@ -1006,7 +971,6 @@ describe("Variant Analysis Manager", () => { await createTimestampFile(variantAnalysisStorageLocation); await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); - writeTextStub; jest.spyOn(env, "clipboard", "get").mockReturnValue({ readText: jest.fn(), writeText: writeTextStub, diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts index 37b66000e..172f8253a 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts @@ -1,9 +1,4 @@ -import { - CancellationToken, - CancellationTokenSource, - commands, - extensions, -} from "vscode"; +import { CancellationTokenSource, commands, extensions } from "vscode"; import { CodeQLExtensionInterface } from "../../../extension"; import * as config from "../../../config"; @@ -20,7 +15,6 @@ import { } from "../../factories/remote-queries/gh-api/variant-analysis-api-response"; import { VariantAnalysis, - VariantAnalysisScannedRepository, VariantAnalysisStatus, } from "../../../remote-queries/shared/variant-analysis"; import { createMockScannedRepos } from "../../factories/remote-queries/gh-api/scanned-repositories"; @@ -37,18 +31,15 @@ jest.setTimeout(60_000); describe("Variant Analysis Monitor", () => { let extension: CodeQLExtensionInterface | Record; - const mockGetVariantAnalysis = jest.spyOn(ghApiClient, "getVariantAnalysis"); + let mockGetVariantAnalysis: jest.SpiedFunction< + typeof ghApiClient.getVariantAnalysis + >; let cancellationTokenSource: CancellationTokenSource; let variantAnalysisMonitor: VariantAnalysisMonitor; let variantAnalysis: VariantAnalysis; let variantAnalysisManager: VariantAnalysisManager; - let mockGetDownloadResult: jest.SpyInstance< - Promise, - [ - scannedRepo: VariantAnalysisScannedRepository, - variantAnalysis: VariantAnalysis, - cancellationToken: CancellationToken, - ] + let mockGetDownloadResult: jest.SpiedFunction< + typeof variantAnalysisManager.autoDownloadVariantAnalysisResult >; beforeEach(async () => { @@ -72,7 +63,9 @@ describe("Variant Analysis Monitor", () => { .spyOn(variantAnalysisManager, "autoDownloadVariantAnalysisResult") .mockResolvedValue(undefined); - mockGetVariantAnalysis.mockRejectedValue(new Error("Not mocked")); + mockGetVariantAnalysis = jest + .spyOn(ghApiClient, "getVariantAnalysis") + .mockRejectedValue(new Error("Not mocked")); limitNumberOfAttemptsToMonitor(); }); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts index c13f23533..4458047e8 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts @@ -100,10 +100,9 @@ describe(VariantAnalysisResultsManager.name, () => { describe("when the artifact_url is present", () => { let arrayBuffer: ArrayBuffer; - const getVariantAnalysisRepoResultStub = jest.spyOn( - ghApiClient, - "getVariantAnalysisRepoResult", - ); + let getVariantAnalysisRepoResultStub: jest.SpiedFunction< + typeof ghApiClient.getVariantAnalysisRepoResult + >; beforeEach(async () => { const sourceFilePath = path.join( @@ -112,14 +111,16 @@ describe(VariantAnalysisResultsManager.name, () => { ); arrayBuffer = fs.readFileSync(sourceFilePath).buffer; - getVariantAnalysisRepoResultStub.mockImplementation( - (_credentials: Credentials, downloadUrl: string) => { - if (downloadUrl === dummyRepoTask.artifactUrl) { - return Promise.resolve(arrayBuffer); - } - return Promise.reject(new Error("Unexpected artifact URL")); - }, - ); + getVariantAnalysisRepoResultStub = jest + .spyOn(ghApiClient, "getVariantAnalysisRepoResult") + .mockImplementation( + (_credentials: Credentials, downloadUrl: string) => { + if (downloadUrl === dummyRepoTask.artifactUrl) { + return Promise.resolve(arrayBuffer); + } + return Promise.reject(new Error("Unexpected artifact URL")); + }, + ); }); it("should call the API to download the results", async () => { diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts index af9cf5d32..ce20460e8 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts @@ -32,10 +32,10 @@ async function showQlDocument(name: string): Promise { } describe("Variant Analysis Submission Integration", () => { - const quickPickSpy = jest.spyOn(window, "showQuickPick"); - const inputBoxSpy = jest.spyOn(window, "showInputBox"); - const executeCommandSpy = jest.spyOn(commands, "executeCommand"); - const showErrorMessageSpy = jest.spyOn(window, "showErrorMessage"); + let quickPickSpy: jest.SpiedFunction; + let inputBoxSpy: jest.SpiedFunction; + let executeCommandSpy: jest.SpiedFunction; + let showErrorMessageSpy: jest.SpiedFunction; beforeEach(async () => { jest.spyOn(config, "isCanary").mockReturnValue(true); @@ -50,11 +50,16 @@ describe("Variant Analysis Submission Integration", () => { await config.setRemoteControllerRepo("github/vscode-codeql"); - quickPickSpy.mockResolvedValue(undefined); - inputBoxSpy.mockResolvedValue(undefined); - - executeCommandSpy.mockRestore(); - showErrorMessageSpy.mockResolvedValue(undefined); + quickPickSpy = jest + .spyOn(window, "showQuickPick") + .mockResolvedValue(undefined); + inputBoxSpy = jest + .spyOn(window, "showInputBox") + .mockResolvedValue(undefined); + executeCommandSpy = jest.spyOn(commands, "executeCommand"); + showErrorMessageSpy = jest + .spyOn(window, "showErrorMessage") + .mockResolvedValue(undefined); await extensions .getExtension>( diff --git a/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts b/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts index 71f9943a7..0efba769c 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts +++ b/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts @@ -111,7 +111,7 @@ const config: Config = { // resolver: undefined, // Automatically restore mock state and implementation before every test - // restoreMocks: false, + restoreMocks: true, // The root directory that Jest should scan for tests and modules within // rootDir: undefined, diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts index 2b15c5ea8..616276261 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/contextual/queryResolver.test.ts @@ -13,12 +13,14 @@ import { CodeQLCliServer } from "../../../cli"; import { DatabaseItem } from "../../../databases"; describe("queryResolver", () => { - const writeFileSpy = jest.spyOn(fs, "writeFile"); + let writeFileSpy: jest.SpiedFunction; - const getQlPackForDbschemeSpy = jest.spyOn(helpers, "getQlPackForDbscheme"); - const getPrimaryDbschemeSpy = jest.spyOn(helpers, "getPrimaryDbscheme"); - jest.spyOn(helpers, "getOnDiskWorkspaceFolders").mockReturnValue([]); - jest.spyOn(helpers, "showAndLogErrorMessage").mockResolvedValue(undefined); + let getQlPackForDbschemeSpy: jest.SpiedFunction< + typeof helpers.getQlPackForDbscheme + >; + let getPrimaryDbschemeSpy: jest.SpiedFunction< + typeof helpers.getPrimaryDbscheme + >; const mockCli = { resolveQueriesInSuite: jest.fn(), @@ -28,15 +30,23 @@ describe("queryResolver", () => { }; beforeEach(() => { - writeFileSpy.mockImplementation(() => Promise.resolve()); + writeFileSpy = jest + .spyOn(fs, "writeFile") + .mockImplementation(() => Promise.resolve()); - getQlPackForDbschemeSpy.mockResolvedValue({ - dbschemePack: "dbschemePack", - dbschemePackIsLibraryPack: false, - }); - getPrimaryDbschemeSpy.mockResolvedValue("primaryDbscheme"); + getQlPackForDbschemeSpy = jest + .spyOn(helpers, "getQlPackForDbscheme") + .mockResolvedValue({ + dbschemePack: "dbschemePack", + dbschemePackIsLibraryPack: false, + }); + getPrimaryDbschemeSpy = jest + .spyOn(helpers, "getPrimaryDbscheme") + .mockResolvedValue("primaryDbscheme"); + + jest.spyOn(helpers, "getOnDiskWorkspaceFolders").mockReturnValue([]); + jest.spyOn(helpers, "showAndLogErrorMessage").mockResolvedValue(undefined); - mockCli.resolveQueriesInSuite; mockCli.cliConstraints.supportsAllowLibraryPacksInResolveQueries.mockReturnValue( true, ); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts index 8929a24d9..87c60c55f 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/databaseFetcher.test.ts @@ -17,7 +17,8 @@ jest.setTimeout(10000); describe("databaseFetcher", () => { describe("convertGithubNwoToDatabaseUrl", () => { - const quickPickSpy = jest.spyOn(window, "showQuickPick"); + let quickPickSpy: jest.SpiedFunction; + const progressSpy = jest.fn(); const mockRequest = jest.fn(); const octokit: Octokit.Octokit = { @@ -25,7 +26,9 @@ describe("databaseFetcher", () => { } as unknown as Octokit.Octokit; beforeEach(() => { - quickPickSpy.mockResolvedValue(undefined); + quickPickSpy = jest + .spyOn(window, "showQuickPick") + .mockResolvedValue(undefined); }); it("should convert a GitHub nwo to a database url", async () => { @@ -129,11 +132,13 @@ describe("databaseFetcher", () => { }); describe("convertLgtmUrlToDatabaseUrl", () => { - const quickPickSpy = jest.spyOn(window, "showQuickPick"); + let quickPickSpy: jest.SpiedFunction; const progressSpy = jest.fn(); beforeEach(() => { - quickPickSpy.mockResolvedValue(undefined); + quickPickSpy = jest + .spyOn(window, "showQuickPick") + .mockResolvedValue(undefined); }); it("should convert a project url to a database url", async () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts index 4fc680f75..8712119ef 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts @@ -197,22 +197,28 @@ describe("Launcher path", () => { const pathToCmd = `abc${path.sep}codeql.cmd`; const pathToExe = `abc${path.sep}codeql.exe`; - 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 warnSpy: jest.SpiedFunction; + let errorSpy: jest.SpiedFunction; + let logSpy: jest.SpiedFunction; + let pathExistsSpy: jest.SpiedFunction; let launcherThatExists = ""; beforeEach(() => { - warnSpy.mockResolvedValue(undefined); - errorSpy.mockResolvedValue(undefined); - logSpy.mockResolvedValue(undefined); - pathExistsSpy.mockImplementation(async (path: string) => { - return path.endsWith(launcherThatExists); - }); - platformSpy.mockReturnValue("win32"); + warnSpy = jest + .spyOn(helpers, "showAndLogWarningMessage") + .mockResolvedValue(undefined); + errorSpy = jest + .spyOn(helpers, "showAndLogErrorMessage") + .mockResolvedValue(undefined); + logSpy = jest.spyOn(logger, "log").mockResolvedValue(undefined); + pathExistsSpy = jest + .spyOn(fs, "pathExists") + .mockImplementation(async (path: string) => { + return path.endsWith(launcherThatExists); + }); + + jest.spyOn(os, "platform").mockReturnValue("win32"); }); it("should not warn with proper launcher name", async () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts index 7a17bbd2d..85a6e1da8 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts @@ -395,13 +395,14 @@ describe("helpers", () => { }); describe("open dialog", () => { - const showInformationMessageSpy = jest.spyOn( - window, - "showInformationMessage", - ); + let showInformationMessageSpy: jest.SpiedFunction< + typeof window.showInformationMessage + >; beforeEach(() => { - showInformationMessageSpy.mockResolvedValue(undefined); + showInformationMessageSpy = jest + .spyOn(window, "showInformationMessage") + .mockResolvedValue(undefined); }); const resolveArg = diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts index e401d2d34..7a2fb1b43 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts @@ -49,14 +49,16 @@ describe("query-history", () => { "mock-extension-location", ); let configListener: QueryHistoryConfigListener; - const showTextDocumentSpy = jest.spyOn(vscode.window, "showTextDocument"); - const showInformationMessageSpy = jest.spyOn( - vscode.window, - "showInformationMessage", - ); - const showQuickPickSpy = jest.spyOn(vscode.window, "showQuickPick"); - const executeCommandSpy = jest.spyOn(vscode.commands, "executeCommand"); - const logSpy = jest.spyOn(logger, "log"); + let showTextDocumentSpy: jest.SpiedFunction< + typeof vscode.window.showTextDocument + >; + let showInformationMessageSpy: jest.SpiedFunction< + typeof vscode.window.showInformationMessage + >; + let showQuickPickSpy: jest.SpiedFunction; + let executeCommandSpy: jest.SpiedFunction< + typeof vscode.commands.executeCommand + >; const doCompareCallback = jest.fn(); let queryHistoryManager: QueryHistoryManager | undefined; @@ -73,12 +75,20 @@ describe("query-history", () => { let variantAnalysisHistory: VariantAnalysisHistoryItem[]; beforeEach(() => { - showTextDocumentSpy.mockResolvedValue(undefined as unknown as TextEditor); + showTextDocumentSpy = jest + .spyOn(vscode.window, "showTextDocument") + .mockResolvedValue(undefined as unknown as TextEditor); + showInformationMessageSpy = jest + .spyOn(vscode.window, "showInformationMessage") + .mockResolvedValue(undefined); + showQuickPickSpy = jest + .spyOn(vscode.window, "showQuickPick") + .mockResolvedValue(undefined); + executeCommandSpy = jest + .spyOn(vscode.commands, "executeCommand") + .mockResolvedValue(undefined); - showInformationMessageSpy.mockResolvedValue(undefined); - showQuickPickSpy.mockResolvedValue(undefined); - executeCommandSpy.mockResolvedValue(undefined); - logSpy.mockResolvedValue(undefined); + jest.spyOn(logger, "log").mockResolvedValue(undefined); tryOpenExternalFile = (QueryHistoryManager.prototype as any) .tryOpenExternalFile; @@ -459,11 +469,9 @@ describe("query-history", () => { describe("handleCancel", () => { let mockCredentials: Credentials; - const credentialsSpy = jest.spyOn(Credentials, "initialize"); - const mockCancelRemoteQuery = jest.spyOn( - ghActionsApiClient, - "cancelRemoteQuery", - ); + let mockCancelRemoteQuery: jest.SpiedFunction< + typeof ghActionsApiClient.cancelRemoteQuery + >; const getOctokitStub = jest.fn(); beforeEach(async () => { @@ -473,10 +481,13 @@ describe("query-history", () => { request: getOctokitStub, }), } as unknown as Credentials; - credentialsSpy.mockResolvedValue(mockCredentials); + jest + .spyOn(Credentials, "initialize") + .mockResolvedValue(mockCredentials); - mockCancelRemoteQuery.mockResolvedValue(); - getOctokitStub; + mockCancelRemoteQuery = jest + .spyOn(ghActionsApiClient, "cancelRemoteQuery") + .mockResolvedValue(); }); describe("if the item is in progress", () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts index 59aa559aa..b859e18ab 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts @@ -69,8 +69,10 @@ describe("Remote queries and query history manager", () => { onVariantAnalysisRemoved: jest.fn(), } as any as VariantAnalysisManager; - const showTextDocumentSpy = jest.spyOn(window, "showTextDocument"); - const openTextDocumentSpy = jest.spyOn(workspace, "openTextDocument"); + let showTextDocumentSpy: jest.SpiedFunction; + let openTextDocumentSpy: jest.SpiedFunction< + typeof workspace.openTextDocument + >; beforeEach(async () => { // Since these tests change the state of the query history manager, we need to copy the original @@ -119,8 +121,12 @@ describe("Remote queries and query history manager", () => { ); disposables.push(qhm); - showTextDocumentSpy.mockResolvedValue(undefined as unknown as TextEditor); - openTextDocumentSpy.mockResolvedValue(undefined as unknown as TextDocument); + showTextDocumentSpy = jest + .spyOn(window, "showTextDocument") + .mockResolvedValue(undefined as unknown as TextEditor); + openTextDocumentSpy = jest + .spyOn(workspace, "openTextDocument") + .mockResolvedValue(undefined as unknown as TextDocument); }); afterEach(() => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts index f7f7b6ba5..0fc2d12d6 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts @@ -6,32 +6,44 @@ import * as config from "../../../config"; import { getRepositorySelection } from "../../../remote-queries/repository-selection"; describe("repository selection", () => { - const quickPickSpy = jest.spyOn(window, "showQuickPick"); - const showInputBoxSpy = jest.spyOn(window, "showInputBox"); + let quickPickSpy: jest.SpiedFunction; + let showInputBoxSpy: jest.SpiedFunction; - const getRemoteRepositoryListsSpy = jest.spyOn( - config, - "getRemoteRepositoryLists", - ); - const getRemoteRepositoryListsPathSpy = jest.spyOn( - config, - "getRemoteRepositoryListsPath", - ); + let getRemoteRepositoryListsSpy: jest.SpiedFunction< + typeof config.getRemoteRepositoryLists + >; + let getRemoteRepositoryListsPathSpy: jest.SpiedFunction< + typeof config.getRemoteRepositoryListsPath + >; - const pathExistsStub = jest.spyOn(fs, "pathExists"); - const fsStatStub = jest.spyOn(fs, "stat"); - const fsReadFileStub = jest.spyOn(fs, "readFile"); + let pathExistsStub: jest.SpiedFunction; + let fsStatStub: jest.SpiedFunction; + let fsReadFileStub: jest.SpiedFunction; beforeEach(() => { - quickPickSpy.mockResolvedValue(undefined); - showInputBoxSpy.mockResolvedValue(undefined); + quickPickSpy = jest + .spyOn(window, "showQuickPick") + .mockResolvedValue(undefined); + showInputBoxSpy = jest + .spyOn(window, "showInputBox") + .mockResolvedValue(undefined); - getRemoteRepositoryListsSpy.mockReturnValue(undefined); - getRemoteRepositoryListsPathSpy.mockReturnValue(undefined); + getRemoteRepositoryListsSpy = jest + .spyOn(config, "getRemoteRepositoryLists") + .mockReturnValue(undefined); + getRemoteRepositoryListsPathSpy = jest + .spyOn(config, "getRemoteRepositoryListsPath") + .mockReturnValue(undefined); - pathExistsStub.mockImplementation(() => false); - fsStatStub.mockRejectedValue(new Error("not found")); - fsReadFileStub.mockRejectedValue(new Error("not found")); + pathExistsStub = jest + .spyOn(fs, "pathExists") + .mockImplementation(() => false); + fsStatStub = jest + .spyOn(fs, "stat") + .mockRejectedValue(new Error("not found")); + fsReadFileStub = jest + .spyOn(fs, "readFile") + .mockRejectedValue(new Error("not found")); }); describe("repo lists from settings", () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts index 06e19fe69..b5b14c457 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/variant-analysis-history.test.ts @@ -67,8 +67,10 @@ describe("Variant Analyses and QueryHistoryManager", () => { showView: showViewStub, } as any as VariantAnalysisManager; - const showTextDocumentSpy = jest.spyOn(window, "showTextDocument"); - const openTextDocumentSpy = jest.spyOn(workspace, "openTextDocument"); + let showTextDocumentSpy: jest.SpiedFunction; + let openTextDocumentSpy: jest.SpiedFunction< + typeof workspace.openTextDocument + >; beforeEach(async () => { // Since these tests change the state of the query history manager, we need to copy the original @@ -101,8 +103,12 @@ describe("Variant Analyses and QueryHistoryManager", () => { ); disposables.push(qhm); - showTextDocumentSpy.mockResolvedValue(undefined as unknown as TextEditor); - openTextDocumentSpy.mockResolvedValue(undefined as unknown as TextDocument); + showTextDocumentSpy = jest + .spyOn(window, "showTextDocument") + .mockResolvedValue(undefined as unknown as TextEditor); + openTextDocumentSpy = jest + .spyOn(workspace, "openTextDocument") + .mockResolvedValue(undefined as unknown as TextDocument); }); afterEach(() => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts index e8f23c674..163b6b1fa 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/run-queries.test.ts @@ -18,10 +18,10 @@ import { LegacyQueryRunner } from "../../legacy-query-server/legacyRunner"; import { DatabaseItem } from "../../databases"; describe("run-queries", () => { - const isCanarySpy = jest.spyOn(config, "isCanary"); + let isCanarySpy: jest.SpiedFunction; beforeEach(() => { - isCanarySpy.mockReturnValue(false); + isCanarySpy = jest.spyOn(config, "isCanary").mockReturnValue(false); }); it("should create a QueryEvaluationInfo", () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts index d717e1294..a8b887fa9 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/telemetry.test.ts @@ -24,20 +24,19 @@ describe("telemetry reporting", () => { 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"); + let sendTelemetryEventSpy: jest.SpiedFunction< + typeof TelemetryReporter.prototype.sendTelemetryEvent + >; + let sendTelemetryExceptionSpy: jest.SpiedFunction< + typeof TelemetryReporter.prototype.sendTelemetryException + >; + let disposeSpy: jest.SpiedFunction< + typeof TelemetryReporter.prototype.dispose + >; - const showInformationMessageSpy = jest.spyOn( - window, - "showInformationMessage", - ); + let showInformationMessageSpy: jest.SpiedFunction< + typeof window.showInformationMessage + >; beforeEach(async () => { try { @@ -49,11 +48,19 @@ describe("telemetry reporting", () => { ctx = createMockExtensionContext(); - sendTelemetryEventSpy.mockReturnValue(undefined); - sendTelemetryExceptionSpy.mockReturnValue(undefined); - disposeSpy.mockResolvedValue(undefined); + sendTelemetryEventSpy = jest + .spyOn(TelemetryReporter.prototype, "sendTelemetryEvent") + .mockReturnValue(undefined); + sendTelemetryExceptionSpy = jest + .spyOn(TelemetryReporter.prototype, "sendTelemetryException") + .mockReturnValue(undefined); + disposeSpy = jest + .spyOn(TelemetryReporter.prototype, "dispose") + .mockResolvedValue(undefined); - showInformationMessageSpy.mockResolvedValue(undefined); + showInformationMessageSpy = jest + .spyOn(window, "showInformationMessage") + .mockResolvedValue(undefined); originalTelemetryExtension = workspace .getConfiguration() From 3f76ad6eb835821920fea8a90b4dd4be9c8c4f5d Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 13:10:42 +0100 Subject: [PATCH 25/58] Fix variant analysis and remote queries tests --- .../remote-queries/remote-queries-manager.test.ts | 9 ++++++--- .../remote-queries/variant-analysis-manager.test.ts | 11 +++++++---- .../remote-queries/variant-analysis-monitor.test.ts | 1 + 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts index 199e76005..6adb83d51 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts @@ -193,7 +193,8 @@ describe("Remote queries", () => { const packNames = packFS.directoryContents(libraryDir).sort(); // check dependencies. - expect(packNames).toEqual(["javascript-all"]); + expect(packNames).toContain("javascript-all"); + expect(packNames.length).toBeLessThan(3); }); it("should run a remote query that is not part of a qlpack", async () => { @@ -249,7 +250,8 @@ describe("Remote queries", () => { const packNames = packFS.directoryContents(libraryDir).sort(); // check dependencies. - expect(packNames).toEqual(["javascript-all"]); + expect(packNames).toContain("javascript-all"); + expect(packNames.length).toBeLessThan(3); }); it("should run a remote query that is nested inside a qlpack", async () => { @@ -308,7 +310,8 @@ describe("Remote queries", () => { const packNames = packFS.directoryContents(libraryDir).sort(); // check dependencies. - expect(packNames).toEqual(["javascript-all"]); + expect(packNames).toContain("javascript-all"); + expect(packNames.length).toBeLessThan(3); }); it("should cancel a run before uploading", async () => { diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts index 4b6aa294d..9a814b50b 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts @@ -61,6 +61,7 @@ describe("Variant Analysis Manager", () => { let pathExistsStub: jest.SpiedFunction; let readJsonStub: jest.SpiedFunction; let outputJsonStub: jest.SpiedFunction; + let writeFileStub: jest.SpiedFunction; let cli: CodeQLCliServer; let cancellationTokenSource: CancellationTokenSource; let variantAnalysisManager: VariantAnalysisManager; @@ -72,13 +73,13 @@ describe("Variant Analysis Manager", () => { pathExistsStub = jest.spyOn(fs, "pathExists"); readJsonStub = jest.spyOn(fs, "readJson"); outputJsonStub = jest.spyOn(fs, "outputJson").mockReturnValue(undefined); + writeFileStub = jest.spyOn(fs, "writeFile").mockReturnValue(undefined); jest.spyOn(logger, "log").mockResolvedValue(undefined); jest .spyOn(config, "isVariantAnalysisLiveResultsEnabled") .mockReturnValue(false); jest.spyOn(fs, "mkdirSync").mockReturnValue(undefined); - jest.spyOn(fs, "writeFile").mockReturnValue(undefined); cancellationTokenSource = new CancellationTokenSource(); @@ -132,6 +133,8 @@ describe("Variant Analysis Manager", () => { } beforeEach(async () => { + writeFileStub.mockRestore(); + // Should not have asked for a language showQuickPickSpy = jest .spyOn(window, "showQuickPick") @@ -293,7 +296,6 @@ describe("Variant Analysis Manager", () => { await variantAnalysisManager.getVariantAnalysis(variantAnalysis.id), ).toEqual(variantAnalysis); - expect(pathExistsStub).toHaveBeenCalledTimes(1); expect(pathExistsStub).toBeCalledWith( path.join(storagePath, variantAnalysis.id.toString()), ); @@ -630,7 +632,7 @@ describe("Variant Analysis Manager", () => { // The actual tests for these are in rehydrateVariantAnalysis, so we can just mock them here and test that // the methods are called. - pathExistsStub.mockImplementation(() => false); + pathExistsStub.mockImplementation(() => true); // This will read in the correct repo states readJsonStub.mockImplementation(() => Promise.resolve({ @@ -651,7 +653,6 @@ describe("Variant Analysis Manager", () => { variantAnalysis, ); - expect(pathExistsStub).toHaveBeenCalledTimes(1); expect(pathExistsStub).toBeCalledWith( path.join(storagePath, variantAnalysis.id.toString()), ); @@ -664,6 +665,8 @@ describe("Variant Analysis Manager", () => { ), ); + pathExistsStub.mockRestore(); + await variantAnalysisManager.autoDownloadVariantAnalysisResult( scannedRepos[0], variantAnalysis, diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts index 172f8253a..9310fc576 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts @@ -223,6 +223,7 @@ describe("Variant Analysis Monitor", () => { index + 1, processScannedRepository(succeededRepo), processUpdatedVariantAnalysis(variantAnalysis, mockApiResponse), + undefined, ); }); }); From 52e2a63828d3bdc10739b5cf9127f81224d33fbd Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 13:21:52 +0100 Subject: [PATCH 26/58] Fix missed `toHaveLength` on object --- .../src/vscode-tests/cli-integration/new-query.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts index 1071f946c..1cc736d0e 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts @@ -242,7 +242,7 @@ describeWithCodeQL()("using the new query server", () => { } await parsedResults.done(); - expect(actualResultSets!).not.toHaveLength(0); + expect(actualResultSets).not.toEqual({}); expect(Object.keys(actualResultSets!).sort()).toEqual( Object.keys(queryTestCase.expectedResultSets).sort(), ); From f830c23018a9947cab6a7ce41974eca73b34806f Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 13:38:40 +0100 Subject: [PATCH 27/58] Fix variant analysis submission integration test Apparently, we're not importing the same `config` file as is used by the actual extension, so mocking methods in this file does not do anything. However, we can mock the `vscode` module, so we can use this for returning different values in the configuration. We also need to mock the authentication session since we don't have one. --- ...nt-analysis-submission-integration.test.ts | 60 +++++++++++++++++-- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts index ce20460e8..0f40d3da7 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-submission-integration.test.ts @@ -1,6 +1,7 @@ import * as path from "path"; import { + authentication, commands, extensions, QuickPickItem, @@ -12,7 +13,6 @@ import * as Octokit from "@octokit/rest"; import { retry } from "@octokit/plugin-retry"; import { CodeQLExtensionInterface } from "../../../extension"; -import * as config from "../../../config"; import { Credentials } from "../../../authentication"; import { MockGitHubApiServer } from "../../../mocks/mock-gh-api-server"; @@ -38,18 +38,66 @@ describe("Variant Analysis Submission Integration", () => { let showErrorMessageSpy: jest.SpiedFunction; beforeEach(async () => { - jest.spyOn(config, "isCanary").mockReturnValue(true); + const originalGetConfiguration = workspace.getConfiguration; + jest - .spyOn(config, "isVariantAnalysisLiveResultsEnabled") - .mockReturnValue(true); + .spyOn(workspace, "getConfiguration") + .mockImplementation((section, scope) => { + const configuration = originalGetConfiguration(section, scope); + + return { + get(key: string, defaultValue?: unknown) { + if (section === "codeQL.variantAnalysis" && key === "liveResults") { + return true; + } + if (section === "codeQL" && key == "canary") { + return true; + } + if ( + section === "codeQL.variantAnalysis" && + key === "controllerRepo" + ) { + return "github/vscode-codeql"; + } + return configuration.get(key, defaultValue); + }, + has(key: string) { + return configuration.has(key); + }, + inspect(key: string) { + return configuration.inspect(key); + }, + update( + key: string, + value: unknown, + configurationTarget?: boolean, + overrideInLanguage?: boolean, + ) { + return configuration.update( + key, + value, + configurationTarget, + overrideInLanguage, + ); + }, + }; + }); + + jest.spyOn(authentication, "getSession").mockResolvedValue({ + id: "test", + accessToken: "test-token", + scopes: [], + account: { + id: "test", + label: "test", + }, + }); const mockCredentials = { getOctokit: () => Promise.resolve(new Octokit.Octokit({ retry })), } as unknown as Credentials; jest.spyOn(Credentials, "initialize").mockResolvedValue(mockCredentials); - await config.setRemoteControllerRepo("github/vscode-codeql"); - quickPickSpy = jest .spyOn(window, "showQuickPick") .mockResolvedValue(undefined); From 91ca9481eb5746d6f14e8d1abc03537b536467ef Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 24 Nov 2022 16:01:20 +0000 Subject: [PATCH 28/58] Migrate minimal-workspace integration tests to Jest (#1786) --- extensions/ql-vscode/jest.config.js | 1 + extensions/ql-vscode/package.json | 2 +- .../minimal-workspace/activation.test.ts | 21 +- .../minimal-workspace/config.test.ts | 39 +-- .../minimal-workspace/databases.test.ts | 244 +++++++++--------- .../databases/db-panel.test.ts | 200 ++++++-------- .../determining-selected-query-test.ts | 35 ++- .../vscode-tests/minimal-workspace/index.ts | 14 - .../jest-runner-vscode.config.ts | 17 ++ .../minimal-workspace/jest.config.ts | 9 + .../qltest-discovery.test.ts | 58 ++--- 11 files changed, 295 insertions(+), 345 deletions(-) delete mode 100644 extensions/ql-vscode/src/vscode-tests/minimal-workspace/index.ts create mode 100644 extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.ts create mode 100644 extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest.config.ts diff --git a/extensions/ql-vscode/jest.config.js b/extensions/ql-vscode/jest.config.js index 0787bf46f..cf577fa5c 100644 --- a/extensions/ql-vscode/jest.config.js +++ b/extensions/ql-vscode/jest.config.js @@ -9,5 +9,6 @@ module.exports = { "/src/view", "/test", "/out/vscode-tests/no-workspace", + "/out/vscode-tests/minimal-workspace", ], }; diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 63477d4e6..8bace70d5 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -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", diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/activation.test.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/activation.test.ts index 6dbf49d74..6a0528a42 100644 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/activation.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/activation.test.ts @@ -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)); diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/config.test.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/config.test.ts index 3b11f6033..c8b53e0cd 100644 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/config.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/config.test.ts @@ -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 { - 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); }); }); }); diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases.test.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases.test.ts index 663bae0f0..b0b125199 100644 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases.test.ts @@ -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, []>; + let registerSpy: jest.Mock, []>; + let deregisterSpy: jest.Mock, []>; + let supportsLanguageNameSpy: jest.Mock, []>; + let resolveDatabaseSpy: jest.Mock, []>; + 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, ); } diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases/db-panel.test.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases/db-panel.test.ts index 99eac9790..cc56c18a5 100644 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases/db-panel.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases/db-panel.test.ts @@ -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); } }); diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/determining-selected-query-test.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/determining-selected-query-test.ts index e3b7be024..4d79287e7 100644 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/determining-selected-query-test.ts +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/determining-selected-query-test.ts @@ -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 { } 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"); }); }); } diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/index.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/index.ts deleted file mode 100644 index 2a50e9324..000000000 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/index.ts +++ /dev/null @@ -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 { - return runTestsInDirectory(__dirname); -} diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.ts new file mode 100644 index 000000000..4de8d4548 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.ts @@ -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; diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest.config.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest.config.ts new file mode 100644 index 000000000..bce274e27 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest.config.ts @@ -0,0 +1,9 @@ +import type { Config } from "jest"; + +import baseConfig from "../jest.config.base"; + +const config: Config = { + ...baseConfig, +}; + +export default config; diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/qltest-discovery.test.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/qltest-discovery.test.ts index d94086d4b..c03203371 100644 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/qltest-discovery.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/qltest-discovery.test.ts @@ -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); }); }); }); From e55b8a366eedab6e87fd6bdd8f11034361f2b8e9 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 17:24:43 +0100 Subject: [PATCH 29/58] Clean up Mocha, Sinon and Chai This removes the Mocha, Sinon and Chai-related packages and removes unused code from the test suite. --- .vscode/launch.json | 64 +- .vscode/settings.json | 2 +- extensions/ql-vscode/package-lock.json | 1175 ----------------- extensions/ql-vscode/package.json | 15 +- .../{forbid-mocha-only => forbid-test-only} | 0 .../ql-vscode/src/vscode-tests/.eslintrc.js | 16 +- .../local-queries/local-query-history-item.ts | 1 - .../src/vscode-tests/index-template.ts | 114 -- .../minimal-workspace/activation.test.ts | 3 - .../no-workspace/query-results.test.ts | 3 - .../ql-vscode/src/vscode-tests/test-config.ts | 23 - 11 files changed, 42 insertions(+), 1374 deletions(-) rename extensions/ql-vscode/scripts/{forbid-mocha-only => forbid-test-only} (100%) delete mode 100644 extensions/ql-vscode/src/vscode-tests/index-template.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index ba462444b..2a3d47f2f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -63,60 +63,52 @@ }, { "name": "Launch Integration Tests - No Workspace (vscode-codeql)", - "type": "extensionHost", + "type": "node", "request": "launch", - "runtimeExecutable": "${execPath}", + "program": "${workspaceFolder}/extensions/ql-vscode/node_modules/jest/bin/jest.js", + "showAsyncStacks": true, + "cwd": "${workspaceFolder}/extensions/ql-vscode", "args": [ - "--extensionDevelopmentPath=${workspaceRoot}/extensions/ql-vscode", - "--extensionTestsPath=${workspaceRoot}/extensions/ql-vscode/out/vscode-tests/no-workspace/index", - "--disable-workspace-trust", - "--disable-extensions", - "--disable-gpu" + "--projects", + "out/vscode-tests/no-workspace" ], + "stopOnEntry": false, "sourceMaps": true, "outFiles": [ "${workspaceRoot}/extensions/ql-vscode/out/**/*.js", ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", }, { "name": "Launch Integration Tests - Minimal Workspace (vscode-codeql)", - "type": "extensionHost", + "type": "node", "request": "launch", - "runtimeExecutable": "${execPath}", + "program": "${workspaceFolder}/extensions/ql-vscode/node_modules/jest/bin/jest.js", + "showAsyncStacks": true, + "cwd": "${workspaceFolder}/extensions/ql-vscode", "args": [ - "--extensionDevelopmentPath=${workspaceRoot}/extensions/ql-vscode", - "--extensionTestsPath=${workspaceRoot}/extensions/ql-vscode/out/vscode-tests/minimal-workspace/index", - "--disable-workspace-trust", - "--disable-extensions", - "--disable-gpu", - "${workspaceRoot}/extensions/ql-vscode/test/data" + "--projects", + "out/vscode-tests/minimal-workspace" ], + "stopOnEntry": false, "sourceMaps": true, "outFiles": [ "${workspaceRoot}/extensions/ql-vscode/out/**/*.js", ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", }, { "name": "Launch Integration Tests - With CLI", - "type": "extensionHost", + "type": "node", "request": "launch", - "runtimeExecutable": "${execPath}", + "program": "${workspaceFolder}/extensions/ql-vscode/node_modules/jest/bin/jest.js", + "showAsyncStacks": true, + "cwd": "${workspaceFolder}/extensions/ql-vscode", "args": [ - "--extensionDevelopmentPath=${workspaceRoot}/extensions/ql-vscode", - "--extensionTestsPath=${workspaceRoot}/extensions/ql-vscode/out/vscode-tests/cli-integration/index", - "--disable-workspace-trust", - "--disable-gpu", - "--disable-extension", - "eamodio.gitlens", - "--disable-extension", - "github.codespaces", - "--disable-extension", - "github.copilot", - "${workspaceRoot}/extensions/ql-vscode/src/vscode-tests/cli-integration/data", - // Uncomment the last line and modify the path to a checked out - // instance of the codeql repository so the libraries are - // available in the workspace for the tests. - // "${workspaceRoot}/../codeql" + "--projects", + "out/vscode-tests/cli-integration" ], "env": { // Optionally, set the version to use for the integration tests. @@ -130,11 +122,19 @@ // If not specified, one will be downloaded automatically. // This option overrides the CLI_VERSION option. // "CLI_PATH": "${workspaceRoot}/../semmle-code/target/intree/codeql/codeql", + + // Uncomment the last line and modify the path to a checked out + // instance of the codeql repository so the libraries are + // available in the workspace for the tests. + // "TEST_CODEQL_PATH": "${workspaceRoot}/../codeql", }, + "stopOnEntry": false, "sourceMaps": true, "outFiles": [ "${workspaceRoot}/extensions/ql-vscode/out/**/*.js", ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", }, { "name": "Launch Storybook", diff --git a/.vscode/settings.json b/.vscode/settings.json index a5e0717fd..aa0ec1a1a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -37,7 +37,7 @@ "javascript.preferences.quoteStyle": "single", "editor.wordWrapColumn": 100, "jest.rootPath": "./extensions/ql-vscode", - "jest.autoRun": "watch", + "jest.autoRun": "off", "jest.nodeEnv": { "LANG": "en-US", "TZ": "UTC" diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index dcdaff1e0..a73576a10 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -67,8 +67,6 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^12.1.5", "@testing-library/user-event": "^14.4.3", - "@types/chai": "^4.1.7", - "@types/chai-as-promised": "~7.1.2", "@types/child-process-promise": "^2.2.1", "@types/classnames": "~2.2.9", "@types/d3": "^7.4.0", @@ -83,17 +81,13 @@ "@types/jest": "^29.0.2", "@types/js-yaml": "^3.12.5", "@types/jszip": "~3.1.6", - "@types/mocha": "^9.0.0", "@types/nanoid": "^3.0.0", "@types/node": "^16.11.25", "@types/node-fetch": "~2.5.2", - "@types/proxyquire": "~1.3.28", "@types/react": "^17.0.2", "@types/react-dom": "^17.0.2", "@types/sarif": "~2.1.2", "@types/semver": "~7.2.0", - "@types/sinon": "~7.5.2", - "@types/sinon-chai": "~3.2.3", "@types/stream-chain": "~2.0.1", "@types/stream-json": "~1.7.1", "@types/tar-stream": "^2.2.2", @@ -110,8 +104,6 @@ "ansi-colors": "^4.1.1", "applicationinsights": "^2.3.5", "babel-loader": "^8.2.5", - "chai": "^4.2.0", - "chai-as-promised": "~7.1.1", "css-loader": "~3.1.0", "del": "^6.0.0", "eslint": "^8.23.1", @@ -133,13 +125,8 @@ "jest-runner-vscode": "^3.0.1", "lint-staged": "~10.2.2", "mini-css-extract-plugin": "^2.6.1", - "mocha": "^10.0.0", - "mocha-sinon": "~2.1.2", "npm-run-all": "^4.1.5", "prettier": "^2.7.1", - "proxyquire": "~2.1.3", - "sinon": "~14.0.0", - "sinon-chai": "~3.5.0", "tar-stream": "^2.2.0", "through2": "^4.0.2", "ts-jest": "^29.0.1", @@ -5931,23 +5918,6 @@ "@sinonjs/commons": "^1.7.0" } }, - "node_modules/@sinonjs/samsam": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", - "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, "node_modules/@storybook/addon-actions": { "version": "6.5.10", "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-6.5.10.tgz", @@ -12814,21 +12784,6 @@ "@babel/types": "^7.3.0" } }, - "node_modules/@types/chai": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz", - "integrity": "sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==", - "dev": true - }, - "node_modules/@types/chai-as-promised": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.3.tgz", - "integrity": "sha512-FQnh1ohPXJELpKhzjuDkPLR2BZCAqed+a6xV4MI/T3XzHfd2FlarfUGUdZYgqYe8oxkYn0fchHEeHfHqdZ96sg==", - "dev": true, - "dependencies": { - "@types/chai": "*" - } - }, "node_modules/@types/child-process-promise": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@types/child-process-promise/-/child-process-promise-2.2.1.tgz", @@ -13464,12 +13419,6 @@ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, - "node_modules/@types/mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", - "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", - "dev": true - }, "node_modules/@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", @@ -13541,12 +13490,6 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, - "node_modules/@types/proxyquire": { - "version": "1.3.28", - "resolved": "https://registry.npmjs.org/@types/proxyquire/-/proxyquire-1.3.28.tgz", - "integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==", - "dev": true - }, "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -13600,22 +13543,6 @@ "@types/node": "*" } }, - "node_modules/@types/sinon": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.2.tgz", - "integrity": "sha512-T+m89VdXj/eidZyejvmoP9jivXgBDdkOSBVQjU9kF349NEx10QdPNGxHeZUaj1IlJ32/ewdyXJjnJxyxJroYwg==", - "dev": true - }, - "node_modules/@types/sinon-chai": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.4.tgz", - "integrity": "sha512-xq5KOWNg70PRC7dnR2VOxgYQ6paumW+4pTZP+6uTSdhpYsAUEeeT5bw6rRHHQrZ4KyR+M5ojOR+lje6TGSpUxA==", - "dev": true, - "dependencies": { - "@types/chai": "*", - "@types/sinon": "*" - } - }, "node_modules/@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -14316,12 +14243,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "node_modules/@vscode/codicons": { "version": "0.0.31", "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.31.tgz", @@ -15490,15 +15411,6 @@ "inherits": "2.0.1" } }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -16821,12 +16733,6 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, "node_modules/browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", @@ -17508,32 +17414,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", - "dev": true, - "dependencies": { - "check-error": "^1.0.2" - } - }, "node_modules/chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", @@ -17610,15 +17490,6 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/cheerio": { "version": "1.0.0-rc.10", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", @@ -19671,18 +19542,6 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -22409,19 +22268,6 @@ "dev": true, "optional": true }, - "node_modules/fill-keys": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", - "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=", - "dev": true, - "dependencies": { - "is-object": "~1.0.1", - "merge-descriptors": "~1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -22607,15 +22453,6 @@ "node": ">= 0.10" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -23167,15 +23004,6 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/get-intrinsic": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", @@ -30335,12 +30163,6 @@ "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", "dev": true }, - "node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", - "dev": true - }, "node_modules/keytar": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", @@ -30831,12 +30653,6 @@ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -31845,339 +31661,6 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, - "node_modules/mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha-sinon": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mocha-sinon/-/mocha-sinon-2.1.2.tgz", - "integrity": "sha512-j6eIQGgOFddcgE1kUFKSvXR9oCuSEiRzv2XUK4iJcntObi2X2vYDvRwvOWxECUZl2dJ+Ciex5fYYni05Lx4azA==", - "dev": true, - "engines": { - "npm": ">1.2" - } - }, - "node_modules/mocha/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/mocha/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/mocha/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mocha/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/mocha/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/mocha/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/yargs/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/module-not-found-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", - "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=", - "dev": true - }, "node_modules/move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -32550,19 +32033,6 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "node_modules/nise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", - "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": ">=5", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - } - }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -33801,21 +33271,6 @@ "node": ">=0.10.0" } }, - "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/path-to-regexp/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, "node_modules/path-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", @@ -33830,15 +33285,6 @@ "node": ">=0.10.0" } }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -34505,17 +33951,6 @@ "node": ">= 0.10" } }, - "node_modules/proxyquire": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.3.tgz", - "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==", - "dev": true, - "dependencies": { - "fill-keys": "^1.0.2", - "module-not-found-error": "^1.0.1", - "resolve": "^1.11.1" - } - }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -36303,60 +35738,6 @@ "simple-concat": "^1.0.0" } }, - "node_modules/sinon": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", - "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.1.2", - "@sinonjs/samsam": "^6.1.1", - "diff": "^5.0.0", - "nise": "^5.1.1", - "supports-color": "^7.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "node_modules/sinon-chai": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.5.0.tgz", - "integrity": "sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==", - "dev": true - }, - "node_modules/sinon/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/sinon/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -40469,12 +39850,6 @@ "microevent.ts": "~0.1.1" } }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, "node_modules/wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", @@ -40681,39 +40056,6 @@ "node": ">=10" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/yargs/node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -45190,23 +44532,6 @@ "@sinonjs/commons": "^1.7.0" } }, - "@sinonjs/samsam": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", - "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, "@storybook/addon-actions": { "version": "6.5.10", "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-6.5.10.tgz", @@ -50345,21 +49670,6 @@ "@babel/types": "^7.3.0" } }, - "@types/chai": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz", - "integrity": "sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==", - "dev": true - }, - "@types/chai-as-promised": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.3.tgz", - "integrity": "sha512-FQnh1ohPXJELpKhzjuDkPLR2BZCAqed+a6xV4MI/T3XzHfd2FlarfUGUdZYgqYe8oxkYn0fchHEeHfHqdZ96sg==", - "dev": true, - "requires": { - "@types/chai": "*" - } - }, "@types/child-process-promise": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@types/child-process-promise/-/child-process-promise-2.2.1.tgz", @@ -50981,12 +50291,6 @@ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, - "@types/mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", - "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", - "dev": true - }, "@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", @@ -51057,12 +50361,6 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, - "@types/proxyquire": { - "version": "1.3.28", - "resolved": "https://registry.npmjs.org/@types/proxyquire/-/proxyquire-1.3.28.tgz", - "integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==", - "dev": true - }, "@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -51116,22 +50414,6 @@ "@types/node": "*" } }, - "@types/sinon": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.2.tgz", - "integrity": "sha512-T+m89VdXj/eidZyejvmoP9jivXgBDdkOSBVQjU9kF349NEx10QdPNGxHeZUaj1IlJ32/ewdyXJjnJxyxJroYwg==", - "dev": true - }, - "@types/sinon-chai": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.4.tgz", - "integrity": "sha512-xq5KOWNg70PRC7dnR2VOxgYQ6paumW+4pTZP+6uTSdhpYsAUEeeT5bw6rRHHQrZ4KyR+M5ojOR+lje6TGSpUxA==", - "dev": true, - "requires": { - "@types/chai": "*", - "@types/sinon": "*" - } - }, "@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -51655,12 +50937,6 @@ "eslint-visitor-keys": "^3.3.0" } }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "@vscode/codicons": { "version": "0.0.31", "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.31.tgz", @@ -52627,12 +51903,6 @@ } } }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -53670,12 +52940,6 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, "browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", @@ -54188,29 +53452,6 @@ "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", "dev": true }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, - "chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", - "dev": true, - "requires": { - "check-error": "^1.0.2" - } - }, "chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", @@ -54268,12 +53509,6 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, "cheerio": { "version": "1.0.0-rc.10", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", @@ -55919,15 +55154,6 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -58037,16 +57263,6 @@ "dev": true, "optional": true }, - "fill-keys": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", - "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=", - "dev": true, - "requires": { - "is-object": "~1.0.1", - "merge-descriptors": "~1.0.0" - } - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -58191,12 +57407,6 @@ "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", "dev": true }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -58622,12 +57832,6 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, "get-intrinsic": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", @@ -64045,12 +63249,6 @@ "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", "dev": true }, - "just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", - "dev": true - }, "keytar": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", @@ -64446,12 +63644,6 @@ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -65269,251 +64461,6 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, - "mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "dependencies": { - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } - } - } - } - }, - "mocha-sinon": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mocha-sinon/-/mocha-sinon-2.1.2.tgz", - "integrity": "sha512-j6eIQGgOFddcgE1kUFKSvXR9oCuSEiRzv2XUK4iJcntObi2X2vYDvRwvOWxECUZl2dJ+Ciex5fYYni05Lx4azA==", - "dev": true - }, - "module-not-found-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", - "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=", - "dev": true - }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -65797,19 +64744,6 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "nise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", - "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": ">=5", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - } - }, "no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -66782,23 +65716,6 @@ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", "dev": true }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, "path-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", @@ -66810,12 +65727,6 @@ "pinkie-promise": "^2.0.0" } }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true - }, "pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -67320,17 +66231,6 @@ "ipaddr.js": "1.9.1" } }, - "proxyquire": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.3.tgz", - "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==", - "dev": true, - "requires": { - "fill-keys": "^1.0.2", - "module-not-found-error": "^1.0.1", - "resolve": "^1.11.1" - } - }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -68723,49 +67623,6 @@ "simple-concat": "^1.0.0" } }, - "sinon": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", - "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.1.2", - "@sinonjs/samsam": "^6.1.1", - "diff": "^5.0.0", - "nise": "^5.1.1", - "supports-color": "^7.2.0" - }, - "dependencies": { - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "sinon-chai": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.5.0.tgz", - "integrity": "sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==", - "dev": true - }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -71925,12 +70782,6 @@ "microevent.ts": "~0.1.1" } }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", @@ -72141,32 +70992,6 @@ "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - } - } - }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 1d55d816a..5f4122611 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1343,8 +1343,6 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^12.1.5", "@testing-library/user-event": "^14.4.3", - "@types/chai": "^4.1.7", - "@types/chai-as-promised": "~7.1.2", "@types/child-process-promise": "^2.2.1", "@types/classnames": "~2.2.9", "@types/d3": "^7.4.0", @@ -1359,17 +1357,13 @@ "@types/jest": "^29.0.2", "@types/js-yaml": "^3.12.5", "@types/jszip": "~3.1.6", - "@types/mocha": "^9.0.0", "@types/nanoid": "^3.0.0", "@types/node": "^16.11.25", "@types/node-fetch": "~2.5.2", - "@types/proxyquire": "~1.3.28", "@types/react": "^17.0.2", "@types/react-dom": "^17.0.2", "@types/sarif": "~2.1.2", "@types/semver": "~7.2.0", - "@types/sinon": "~7.5.2", - "@types/sinon-chai": "~3.2.3", "@types/stream-chain": "~2.0.1", "@types/stream-json": "~1.7.1", "@types/tar-stream": "^2.2.2", @@ -1386,8 +1380,6 @@ "ansi-colors": "^4.1.1", "applicationinsights": "^2.3.5", "babel-loader": "^8.2.5", - "chai": "^4.2.0", - "chai-as-promised": "~7.1.1", "css-loader": "~3.1.0", "del": "^6.0.0", "eslint": "^8.23.1", @@ -1409,13 +1401,8 @@ "jest-runner-vscode": "^3.0.1", "lint-staged": "~10.2.2", "mini-css-extract-plugin": "^2.6.1", - "mocha": "^10.0.0", - "mocha-sinon": "~2.1.2", "npm-run-all": "^4.1.5", "prettier": "^2.7.1", - "proxyquire": "~2.1.3", - "sinon": "~14.0.0", - "sinon-chai": "~3.5.0", "tar-stream": "^2.2.0", "through2": "^4.0.2", "ts-jest": "^29.0.1", @@ -1431,7 +1418,7 @@ "husky": { "hooks": { "pre-commit": "npm run format-staged", - "pre-push": "npm run lint && scripts/forbid-mocha-only" + "pre-push": "npm run lint && scripts/forbid-test-only" } }, "lint-staged": { diff --git a/extensions/ql-vscode/scripts/forbid-mocha-only b/extensions/ql-vscode/scripts/forbid-test-only similarity index 100% rename from extensions/ql-vscode/scripts/forbid-mocha-only rename to extensions/ql-vscode/scripts/forbid-test-only diff --git a/extensions/ql-vscode/src/vscode-tests/.eslintrc.js b/extensions/ql-vscode/src/vscode-tests/.eslintrc.js index 502547c1c..f4495e803 100644 --- a/extensions/ql-vscode/src/vscode-tests/.eslintrc.js +++ b/extensions/ql-vscode/src/vscode-tests/.eslintrc.js @@ -3,7 +3,7 @@ module.exports = { project: ["../../tsconfig.json"], }, env: { - mocha: true + jest: true, }, rules: { "@typescript-eslint/ban-types": [ @@ -12,11 +12,11 @@ module.exports = { // For a full list of the default banned types, see: // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/ban-types.md extendDefaults: true, - "types": { + types: { // Don't complain about the `Function` type in test files. (Default is `true`.) - "Function": false, - } - } - ] - } -} + Function: false, + }, + }, + ], + }, +}; diff --git a/extensions/ql-vscode/src/vscode-tests/factories/local-queries/local-query-history-item.ts b/extensions/ql-vscode/src/vscode-tests/factories/local-queries/local-query-history-item.ts index 12db83a64..8d4092c73 100644 --- a/extensions/ql-vscode/src/vscode-tests/factories/local-queries/local-query-history-item.ts +++ b/extensions/ql-vscode/src/vscode-tests/factories/local-queries/local-query-history-item.ts @@ -70,7 +70,6 @@ export function createMockQueryWithResults({ hasInterpretedResults = true, hasMetadata = undefined, }: { - sandbox?: sinon.SinonSandbox; didRunSuccessfully?: boolean; hasInterpretedResults?: boolean; hasMetadata?: boolean; diff --git a/extensions/ql-vscode/src/vscode-tests/index-template.ts b/extensions/ql-vscode/src/vscode-tests/index-template.ts deleted file mode 100644 index 7cc2b1a06..000000000 --- a/extensions/ql-vscode/src/vscode-tests/index-template.ts +++ /dev/null @@ -1,114 +0,0 @@ -import * as path from "path"; -import * as Mocha from "mocha"; -import * as glob from "glob-promise"; -import { ensureCli } from "./ensureCli"; -import { env } from "vscode"; -import { testConfigHelper } from "./test-config"; - -// Use this handler to avoid swallowing unhandled rejections. -process.on("unhandledRejection", (e) => { - console.error("Unhandled rejection."); - console.error(e); - // Must use a setTimeout in order to ensure the log is fully flushed before exiting - setTimeout(() => { - process.exit(-1); - }, 2000); -}); - -process.on("exit", (code) => { - // If the exit code is 7, then the test runner has completed, but - // there was an error in exiting vscode. - if (code === 7) { - console.warn( - "Attempted to exit with code 7. This is likely due to a failure to exit vscode. Ignoring this and exiting with code 0.", - ); - process.exit(0); - } -}); - -/** - * Helper function that runs all Mocha tests found in the - * given test root directory. - * - * For each integration test suite, `vscode-test` expects - * a test runner script exporting a function with the signature: - * ```ts - * export function run(): Promise - * ``` - * - * To create an integration test suite: - * - create a directory beside this file - * - create integration tests in the directory, named `.test.ts` - * - create an `index.ts` file in the directory, containing: - * ```ts - * import { runTestsInDirectory } from '../index-template'; - * export function run(): Promise { - * return runTestsInDirectory(__dirname); - * } - * ``` - * - * After https://github.com/microsoft/TypeScript/issues/420 is implemented, - * this pattern can be expressed more neatly using a module interface. - */ -export async function runTestsInDirectory( - testsRoot: string, - useCli = false, -): Promise { - // Create the mocha test - const mocha = new Mocha({ - ui: "bdd", - color: true, - globalSetup: [], - globalTeardown: [], - } as any); - - (mocha.options as any).globalSetup.push( - // convert this function into an noop since it should not run during tests. - // If it does run during tests, then it can cause some testing environments - // to hang. - ((env as any).openExternal = () => { - /**/ - }), - ); - - await ensureCli(useCli); - - console.log(`Adding test cases and helpers from ${testsRoot}`); - - const files = await glob("**/**.js", { cwd: testsRoot }); - - // Add test files to the test suite - files - .filter((f) => f.endsWith(".test.js")) - .forEach((f) => { - console.log(`Adding test file ${f}`); - mocha.addFile(path.resolve(testsRoot, f)); - }); - - // Setup the config helper. This needs to run before other helpers so any config they setup - // is restored. - await testConfigHelper(mocha); - - // Add helpers. Helper files add global setup and teardown blocks - // for a test run. - files - .filter((f) => f.endsWith(".helper.js")) - .forEach((f) => { - console.log(`Executing helper ${f}`); - // eslint-disable-next-line @typescript-eslint/no-var-requires - const helper = require(path.resolve(testsRoot, f)).default; - helper(mocha); - }); - - return new Promise((resolve, reject) => { - // Run the mocha test - mocha.run((failures) => { - if (failures > 0) { - reject(new Error(`${failures} tests failed.`)); - return; - } - - resolve(); - }); - }); -} diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/activation.test.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/activation.test.ts index 6a0528a42..c897d8d7d 100644 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/activation.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/activation.test.ts @@ -2,9 +2,6 @@ import * as path from "path"; import * as vscode from "vscode"; import * as determiningSelectedQueryTest from "./determining-selected-query-test"; -// 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", () => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts index 9a578d7ac..3ba247e0a 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts @@ -26,9 +26,6 @@ import { import { EvaluationResult, QueryResultType } from "../../pure/legacy-messages"; import { sleep } from "../../pure/time"; -// Temporary until Mocha is fully removed. This is necessary for passing timeouts to `it`. -declare let it: jest.It; - describe("query-results", () => { let queryPath: string; let cnt = 0; diff --git a/extensions/ql-vscode/src/vscode-tests/test-config.ts b/extensions/ql-vscode/src/vscode-tests/test-config.ts index 4bb9ac371..01d12c8a7 100644 --- a/extensions/ql-vscode/src/vscode-tests/test-config.ts +++ b/extensions/ql-vscode/src/vscode-tests/test-config.ts @@ -105,29 +105,6 @@ export const getTestSetting = ( return TEST_SETTINGS.find((testSetting) => testSetting.setting === setting); }; -export const testConfigHelper = async (mocha: Mocha) => { - // Allow extra time to read settings. Sometimes this can time out. - mocha.timeout(20000); - - // Read in all current settings - await Promise.all(TEST_SETTINGS.map((setting) => setting.initialSetup())); - mocha.rootHooks({ - async beforeEach() { - // Reset the settings to their initial values before each test - await Promise.all(TEST_SETTINGS.map((setting) => setting.setup())); - }, - async afterAll() { - // Restore all settings to their default values after each test suite - // Only do this outside of CI since the sometimes hangs on CI. - if (process.env.CI !== "true") { - await Promise.all( - TEST_SETTINGS.map((setting) => setting.restoreToInitialValues()), - ); - } - }, - }); -}; - export const jestTestConfigHelper = async () => { // Read in all current settings await Promise.all(TEST_SETTINGS.map((setting) => setting.initialSetup())); From aa5026f192fc384ee157c191d94d2a16276ed393 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 25 Nov 2022 10:11:19 +0100 Subject: [PATCH 30/58] Use `patch-package` to add Windows support This will apply the same change as the PR on `jest-runner-vscode` in the local `node_modules` on every install, ensuring that we have Windows support. --- extensions/ql-vscode/package-lock.json | 501 +++++++++++++++++- extensions/ql-vscode/package.json | 4 +- .../patches/jest-runner-vscode+3.0.1.patch | 19 + 3 files changed, 517 insertions(+), 7 deletions(-) create mode 100644 extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index dcdaff1e0..56307aadb 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "vscode-codeql", "version": "1.7.7", + "hasInstallScript": true, "license": "MIT", "dependencies": { "@octokit/plugin-retry": "^3.0.9", @@ -136,6 +137,7 @@ "mocha": "^10.0.0", "mocha-sinon": "~2.1.2", "npm-run-all": "^4.1.5", + "patch-package": "^6.5.0", "prettier": "^2.7.1", "proxyquire": "~2.1.3", "sinon": "~14.0.0", @@ -14696,6 +14698,12 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, "node_modules/@zxing/text-encoding": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", @@ -22567,6 +22575,73 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dev": true, + "dependencies": { + "micromatch": "^4.0.2" + } + }, + "node_modules/find-yarn-workspace-root/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-yarn-workspace-root/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-yarn-workspace-root/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/find-yarn-workspace-root/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/find-yarn-workspace-root/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/findup-sync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", @@ -30361,6 +30436,15 @@ "node": ">=0.10.0" } }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -33734,6 +33818,199 @@ "node": ">=0.10.0" } }, + "node_modules/patch-package": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.0.tgz", + "integrity": "sha512-tC3EqJmo74yKqfsMzELaFwxOAu6FH6t+FzFOsnWAuARm7/n2xB5AOeOueE221eM9gtMuIKMKpF9tBy/X2mNP0Q==", + "dev": true, + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "cross-spawn": "^6.0.5", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^7.0.1", + "is-ci": "^2.0.0", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^5.6.0", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^1.10.2" + }, + "bin": { + "patch-package": "index.js" + }, + "engines": { + "node": ">=10", + "npm": ">5" + } + }, + "node_modules/patch-package/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/patch-package/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/patch-package/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/patch-package/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/patch-package/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/patch-package/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/patch-package/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/patch-package/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/patch-package/node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/patch-package/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/patch-package/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/patch-package/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/patch-package/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/patch-package/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -40643,9 +40920,9 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "node_modules/yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true, "engines": { "node": ">= 6" @@ -52012,6 +52289,12 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, "@zxing/text-encoding": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", @@ -58160,6 +58443,60 @@ "semver-regex": "^3.1.2" } }, + "find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dev": true, + "requires": { + "micromatch": "^4.0.2" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, "findup-sync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", @@ -64067,6 +64404,15 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11" + } + }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -66730,6 +67076,149 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "patch-package": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.0.tgz", + "integrity": "sha512-tC3EqJmo74yKqfsMzELaFwxOAu6FH6t+FzFOsnWAuARm7/n2xB5AOeOueE221eM9gtMuIKMKpF9tBy/X2mNP0Q==", + "dev": true, + "requires": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "cross-spawn": "^6.0.5", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^7.0.1", + "is-ci": "^2.0.0", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^5.6.0", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^1.10.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } + } + }, "path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -72056,9 +72545,9 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true }, "yargs": { diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 1d55d816a..73bcab1a0 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1282,7 +1282,8 @@ "format-staged": "lint-staged", "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook", - "lint:scenarios": "ts-node scripts/lint-scenarios.ts" + "lint:scenarios": "ts-node scripts/lint-scenarios.ts", + "postinstall": "patch-package" }, "dependencies": { "@octokit/plugin-retry": "^3.0.9", @@ -1412,6 +1413,7 @@ "mocha": "^10.0.0", "mocha-sinon": "~2.1.2", "npm-run-all": "^4.1.5", + "patch-package": "^6.5.0", "prettier": "^2.7.1", "proxyquire": "~2.1.3", "sinon": "~14.0.0", diff --git a/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch b/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch new file mode 100644 index 000000000..9e01a48d2 --- /dev/null +++ b/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch @@ -0,0 +1,19 @@ +diff --git a/node_modules/jest-runner-vscode/dist/child/runner.js b/node_modules/jest-runner-vscode/dist/child/runner.js +index 0663c5c..4991663 100644 +--- a/node_modules/jest-runner-vscode/dist/child/runner.js ++++ b/node_modules/jest-runner-vscode/dist/child/runner.js +@@ -18,10 +18,13 @@ async function run() { + const ipc = new ipc_client_1.default('child'); + const disconnected = new Promise(resolve => ipc.on('disconnect', resolve)); + try { +- const { PARENT_JEST_OPTIONS } = process_1.default.env; ++ const { PARENT_JEST_OPTIONS, PARENT_CWD } = process_1.default.env; + if (!PARENT_JEST_OPTIONS) { + throw new Error('PARENT_JEST_OPTIONS is not defined'); + } ++ if (PARENT_CWD) { ++ process_1.default.chdir(PARENT_CWD); ++ } + const options = JSON.parse(PARENT_JEST_OPTIONS); + const jestOptions = [ + ...options.args, From 4be69e858c6f18659095d3e1468554e190f25485 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 25 Nov 2022 10:41:22 +0100 Subject: [PATCH 31/58] Add debugging support for Jest integration tests Since we are launching a completely different process for the extension tests than the process that is launched by VSCode, we need to add some special handling for the debugging. This will let the extension host/VSCode expose a debugging port, which VSCode will then connect to. This is "less desirable than letting the bootloader do its thing", but a packaged VSCode application does not allow using the bootloader (`NODE_OPTIONS`=`--require=...`). Therefore, we have to fallback to this option. See: https://github.com/microsoft/vscode-js-debug/blob/47c60558ec31902f42c255abb9b460078df02f9d/src/configuration.ts#L405-L411 --- .vscode/launch.json | 15 +++------------ .../jest-runner-vscode.config.base.ts | 4 ++++ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 2a3d47f2f..b88fafd51 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -72,13 +72,10 @@ "--projects", "out/vscode-tests/no-workspace" ], - "stopOnEntry": false, "sourceMaps": true, - "outFiles": [ - "${workspaceRoot}/extensions/ql-vscode/out/**/*.js", - ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", + "attachSimplePort": 9223, }, { "name": "Launch Integration Tests - Minimal Workspace (vscode-codeql)", @@ -91,13 +88,10 @@ "--projects", "out/vscode-tests/minimal-workspace" ], - "stopOnEntry": false, "sourceMaps": true, - "outFiles": [ - "${workspaceRoot}/extensions/ql-vscode/out/**/*.js", - ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", + "attachSimplePort": 9223, }, { "name": "Launch Integration Tests - With CLI", @@ -128,13 +122,10 @@ // available in the workspace for the tests. // "TEST_CODEQL_PATH": "${workspaceRoot}/../codeql", }, - "stopOnEntry": false, "sourceMaps": true, - "outFiles": [ - "${workspaceRoot}/extensions/ql-vscode/out/**/*.js", - ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", + "attachSimplePort": 9223, }, { "name": "Launch Storybook", diff --git a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts index c390eceb2..71e3ccd1b 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts +++ b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts @@ -16,4 +16,8 @@ const config: RunnerOptions = { extensionDevelopmentPath: rootDir, }; +if (process.env.VSCODE_INSPECTOR_OPTIONS) { + config.launchArgs?.push("--inspect-extensions", "9223"); +} + export default config; From 430bcd0aa23f768157c089b59e85e44d7e46274f Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 25 Nov 2022 10:59:50 +0100 Subject: [PATCH 32/58] Increase timeout for legacy query server tests --- .../src/vscode-tests/cli-integration/legacy-query.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts index 0161328e3..d5254aa90 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts @@ -98,7 +98,7 @@ const db: messages.Dataset = { workingSet: "default", }; -jest.setTimeout(20_000); +jest.setTimeout(60_000); describeWithCodeQL()("using the legacy query server", () => { const nullProgressReporter: ProgressReporter = { From 51425915d9cd7c66bc0e2a991253f3bc3083429d Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 25 Nov 2022 11:26:44 +0100 Subject: [PATCH 33/58] Use `fs-extras` and async methods for file manipulation --- .../no-workspace/query-results.test.ts | 4 +++- .../remote-query-history.test.ts | 18 +++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts index 9a578d7ac..d0469f5b7 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts @@ -189,9 +189,11 @@ describe("query-results", () => { const interpretedResultsPath = path.join(tmpDir.name, "interpreted.json"); const sourceInfo = {}; - beforeEach(() => { + beforeEach(async () => { spy.mockReturnValue({ a: "1234" }); + await fs.ensureDir(path.basename(interpretedResultsPath)); + mockServer = { interpretBqrsSarif: spy, } as unknown as CodeQLCliServer; diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts index b859e18ab..a7cfc410f 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts @@ -129,8 +129,8 @@ describe("Remote queries and query history manager", () => { .mockResolvedValue(undefined as unknown as TextDocument); }); - afterEach(() => { - deleteHistoryState(); + afterEach(async () => { + await deleteHistoryState(); disposables.dispose(testDisposeHandler); }); @@ -505,8 +505,9 @@ describe("Remote queries and query history manager", () => { }); async function copyHistoryState() { - fs.ensureDirSync(STORAGE_DIR); - fs.copySync( + await fs.ensureDir(STORAGE_DIR); + await fs.ensureDir(path.join(tmpDir.name, "remote-queries")); + await fs.copy( path.join(__dirname, "../data/remote-queries/"), path.join(tmpDir.name, "remote-queries"), ); @@ -517,13 +518,8 @@ describe("Remote queries and query history manager", () => { } } - function deleteHistoryState() { - fs.rmSync(STORAGE_DIR, { - recursive: true, - force: true, - maxRetries: 10, - retryDelay: 100, - }); + async function deleteHistoryState() { + await fs.remove(STORAGE_DIR); } function replacePlaceholder(filePath: string) { From a23a8f78ddda4a592828bc272c87434762cf600d Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 25 Nov 2022 13:06:03 +0100 Subject: [PATCH 34/58] Use ts-jest for running integration tests Instead of running the integration tests from the `out` directory, this will run the integration tests from the `src` directory using `ts-jest`. Unfortunately, we are not able to use TypeScript files for the `jest-runner-vscode` configuration since `cosmiconfig` (the package that handles the configuration loading for `jest-runner-vscode`) doesn't support loading TypeScript files by default. --- extensions/ql-vscode/jest.config.js | 6 +++--- extensions/ql-vscode/package.json | 6 +++--- ....config.ts => jest-runner-vscode.config.js} | 14 +++++++------- .../cli-integration/jest.config.ts | 4 ++-- .../cli-integration/legacy-query.test.ts | 2 +- ...se.ts => jest-runner-vscode.config.base.js} | 18 +++++++++++------- .../src/vscode-tests/jest.config.base.ts | 4 ++-- .../jest-runner-vscode.config.js | 18 ++++++++++++++++++ .../jest-runner-vscode.config.ts | 17 ----------------- .../no-workspace/jest-runner-vscode.config.js | 9 +++++++++ .../no-workspace/jest-runner-vscode.config.ts | 12 ------------ 11 files changed, 56 insertions(+), 54 deletions(-) rename extensions/ql-vscode/src/vscode-tests/cli-integration/{jest-runner-vscode.config.ts => jest-runner-vscode.config.js} (63%) rename extensions/ql-vscode/src/vscode-tests/{jest-runner-vscode.config.base.ts => jest-runner-vscode.config.base.js} (54%) create mode 100644 extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.js delete mode 100644 extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.ts create mode 100644 extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.js delete mode 100644 extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts diff --git a/extensions/ql-vscode/jest.config.js b/extensions/ql-vscode/jest.config.js index 2015ae66d..eb44660a8 100644 --- a/extensions/ql-vscode/jest.config.js +++ b/extensions/ql-vscode/jest.config.js @@ -8,8 +8,8 @@ module.exports = { projects: [ "/src/view", "/test", - "/out/vscode-tests/cli-integration", - "/out/vscode-tests/no-workspace", - "/out/vscode-tests/minimal-workspace", + "/src/vscode-tests/cli-integration", + "/src/vscode-tests/no-workspace", + "/src/vscode-tests/minimal-workspace", ], }; diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 5f4122611..07a8db5cf 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1273,9 +1273,9 @@ "test:unit": "jest --projects test", "test:view": "jest --projects src/view", "integration": "npm-run-all integration:*", - "integration:no-workspace": "jest --projects out/vscode-tests/no-workspace", - "integration:minimal-workspace": "jest --projects out/vscode-tests/minimal-workspace", - "cli-integration": "jest --projects out/vscode-tests/cli-integration", + "integration:no-workspace": "jest --projects src/vscode-tests/no-workspace", + "integration:minimal-workspace": "jest --projects src/vscode-tests/minimal-workspace", + "cli-integration": "jest --projects src/vscode-tests/cli-integration", "update-vscode": "node ./node_modules/vscode/bin/install", "format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix", "lint": "eslint . --ext .ts,.tsx --max-warnings=0", diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.js similarity index 63% rename from extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.ts rename to extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.js index 672794e59..ec56deb64 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.js @@ -1,10 +1,12 @@ -import * as path from "path"; +const path = require("path"); -import { RunnerOptions } from "jest-runner-vscode"; +const { + config: baseConfig, + rootDir, +} = require("../jest-runner-vscode.config.base"); -import baseConfig, { rootDir } from "../jest-runner-vscode.config.base"; - -const config: RunnerOptions = { +/** @type import("jest-runner-vscode").RunnerOptions */ +const config = { ...baseConfig, launchArgs: [ ...(baseConfig.launchArgs ?? []), @@ -25,6 +27,4 @@ const config: RunnerOptions = { }, }; -// 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; diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.config.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.config.ts index eb7367100..3a419bc65 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.config.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest.config.ts @@ -4,8 +4,8 @@ import baseConfig from "../jest.config.base"; const config: Config = { ...baseConfig, - runner: "/jest-runner-cli-integration.js", - setupFilesAfterEnv: ["/jest.setup.js"], + runner: "/jest-runner-cli-integration.ts", + setupFilesAfterEnv: ["/jest.setup.ts"], }; export default config; diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts index 0161328e3..5ad0db9ed 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts @@ -94,7 +94,7 @@ const queryTestCases: QueryTestCase[] = [ ]; const db: messages.Dataset = { - dbDir: path.join(__dirname, "../test-db"), + dbDir: path.join(__dirname, "../../../.vscode-test/test-db"), workingSet: "default", }; diff --git a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.js similarity index 54% rename from extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts rename to extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.js index 71e3ccd1b..71009f786 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.ts +++ b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.js @@ -1,12 +1,12 @@ -import * as path from "path"; -import * as tmp from "tmp-promise"; -import { RunnerOptions } from "jest-runner-vscode"; +const path = require("path"); +const tmp = require("tmp-promise"); -export const tmpDir = tmp.dirSync({ unsafeCleanup: true }); +const tmpDir = tmp.dirSync({ unsafeCleanup: true }); -export const rootDir = path.resolve(__dirname, "../.."); +const rootDir = path.resolve(__dirname, "../.."); -const config: RunnerOptions = { +/** @type import("jest-runner-vscode").RunnerOptions */ +const config = { version: "stable", launchArgs: [ "--disable-gpu", @@ -20,4 +20,8 @@ if (process.env.VSCODE_INSPECTOR_OPTIONS) { config.launchArgs?.push("--inspect-extensions", "9223"); } -export default config; +module.exports = { + config, + tmpDir, + rootDir, +}; diff --git a/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts b/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts index 0efba769c..40846df79 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts +++ b/extensions/ql-vscode/src/vscode-tests/jest.config.base.ts @@ -93,7 +93,7 @@ const config: Config = { // notifyMode: "failure-change", // A preset that is used as a base for Jest's configuration - // preset: 'ts-jest', + preset: "ts-jest", // Run tests from one or more projects // projects: undefined, @@ -128,7 +128,7 @@ const config: Config = { // setupFiles: [], // A list of paths to modules that run some code to configure or set up the testing framework before each test - setupFilesAfterEnv: ["/../jest.setup.js"], + setupFilesAfterEnv: ["/../jest.setup.ts"], // The number of seconds after which a test is considered as slow and reported as such in the results. // slowTestThreshold: 5, diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.js b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.js new file mode 100644 index 000000000..027ec79b2 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.js @@ -0,0 +1,18 @@ +const path = require("path"); + +const { + config: baseConfig, + rootDir, +} = require("../jest-runner-vscode.config.base"); + +/** @type import("jest-runner-vscode").RunnerOptions */ +const config = { + ...baseConfig, + launchArgs: [ + ...(baseConfig.launchArgs ?? []), + "--disable-extensions", + path.resolve(rootDir, "test/data"), + ], +}; + +module.exports = config; diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.ts deleted file mode 100644 index 4de8d4548..000000000 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.ts +++ /dev/null @@ -1,17 +0,0 @@ -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; diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.js b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.js new file mode 100644 index 000000000..d41dccb07 --- /dev/null +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.js @@ -0,0 +1,9 @@ +const { config: baseConfig } = require("../jest-runner-vscode.config.base"); + +/** @type import("jest-runner-vscode").RunnerOptions */ +const config = { + ...baseConfig, + launchArgs: [...(baseConfig.launchArgs ?? []), "--disable-extensions"], +}; + +module.exports = config; diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts deleted file mode 100644 index 0d0108474..000000000 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { RunnerOptions } from "jest-runner-vscode"; - -import baseConfig from "../jest-runner-vscode.config.base"; - -const config: RunnerOptions = { - ...baseConfig, - launchArgs: [...(baseConfig.launchArgs ?? []), "--disable-extensions"], -}; - -// 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; From 003b9dcf2ca87f072232ea1f6744f235dc911756 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Fri, 25 Nov 2022 12:24:40 +0000 Subject: [PATCH 35/58] Add launch configuration to run single unit test (#1796) --- .vscode/launch.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index b88fafd51..adfeef6c9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -45,6 +45,30 @@ "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" }, + { + "name": "Launch Selected Unit Test (vscode-codeql)", + "type": "node", + "request": "launch", + "program": "${workspaceFolder}/extensions/ql-vscode/node_modules/jest/bin/jest.js", + "showAsyncStacks": true, + "cwd": "${workspaceFolder}/extensions/ql-vscode", + "env": { + "LANG": "en-US", + "TZ": "UTC" + }, + "args": [ + "--projects", + "test", + "-i", + "${relativeFile}", + "-t", + "${selectedText}" + ], + "stopOnEntry": false, + "sourceMaps": true, + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + }, { "name": "Launch Unit Tests - React (vscode-codeql)", "type": "node", From 927817f99d317adedf6910e361b7e471edd4ba40 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 25 Nov 2022 12:05:19 +0100 Subject: [PATCH 36/58] Add debugging support to jest-runner This adds debugging support to jest-runner for the integration tests when they are run from the `out` directory. Unfortunately, this removes the ability to debug the non-integration tests, such as the pure tests. --- .vscode/extensions.json | 1 + .vscode/settings.json | 6 ++++++ .../src/vscode-tests/jest-runner-vscode.config.base.js | 6 +++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 469c5cd55..3f9c63680 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -6,6 +6,7 @@ "amodio.tsl-problem-matcher", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", + "firsttris.vscode-jest-runner", "Orta.vscode-jest", ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. diff --git a/.vscode/settings.json b/.vscode/settings.json index aa0ec1a1a..7c476e785 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -42,6 +42,12 @@ "LANG": "en-US", "TZ": "UTC" }, + "jestrunner.debugOptions": { + "attachSimplePort": 9223, + "env": { + "VSCODE_WAIT_FOR_DEBUGGER": "true", + } + }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, diff --git a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.js b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.js index 71009f786..f8e1a45cb 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.js +++ b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.js @@ -17,7 +17,11 @@ const config = { }; if (process.env.VSCODE_INSPECTOR_OPTIONS) { - config.launchArgs?.push("--inspect-extensions", "9223"); + if (process.env.VSCODE_WAIT_FOR_DEBUGGER === "true") { + config.launchArgs?.push("--inspect-brk-extensions", "9223"); + } else { + config.launchArgs?.push("--inspect-extensions", "9223"); + } } module.exports = { From 24a61d387dbee5d7af4942dcf2accf0dcfed8b3c Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 25 Nov 2022 13:34:19 +0100 Subject: [PATCH 37/58] Add documentation for running a single test --- .vscode/settings.json | 13 +++++++------ CONTRIBUTING.md | 26 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7c476e785..eda3a9441 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -42,12 +42,13 @@ "LANG": "en-US", "TZ": "UTC" }, - "jestrunner.debugOptions": { - "attachSimplePort": 9223, - "env": { - "VSCODE_WAIT_FOR_DEBUGGER": "true", - } - }, + // Uncomment to debug integration tests + // "jestrunner.debugOptions": { + // "attachSimplePort": 9223, + // "env": { + // "VSCODE_WAIT_FOR_DEBUGGER": "true", + // } + // }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 42220cb9c..95d4e4005 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -150,6 +150,32 @@ The CLI integration tests require the CodeQL standard libraries in order to run 3. Run the VSCode task from the "Run and Debug" view called _Launch Integration Tests - With CLI_. +#### Running a single test + +##### 1. From the terminal + +The easiest way to run a single test is to change the `it` of the test to `it.only` and then run the test command with some additional options +to only run tests for this specific file. For example, to run the test `src/vscode-tests/cli-integration/run-queries.test.ts`: + +```shell +npm run cli-integration -- --runTestsByPath src/vscode-tests/cli-integration/run-queries.test.ts +``` + +You can also use the `--testNamePattern` option to run a specific test within a file. For example, to run the test `src/vscode-tests/cli-integration/run-queries.test.ts`: + +```shell +npm run cli-integration -- --runTestsByPath src/vscode-tests/cli-integration/run-queries.test.ts --testNamePattern "should create a QueryEvaluationInfo" +``` + +##### 2. From VSCode + +Alternatively, you can run a single test inside VSCode. To do so, install the [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) extension. Then, +you will have quicklinks to run a single test from within test files. To run a single unit or integration test, click the "Run" button. Debugging a single test is currently only supported +for unit tests by default. To debug integration tests, open the `.vscode/settings.json` file and uncomment the `jestrunner.debugOptions` lines. This will allow you to debug integration tests. +Please make sure to revert this change before committing; with this setting enabled, it is not possible to debug unit tests. + +Without the Jest Runner extension, you can also use the "Launch Selected Unit Test (vscode-codeql)" launch configuration to run a single unit test. + #### Using a mock GitHub API server Multi-Repo Variant Analyses (MRVA) rely on the GitHub API. In order to make development and testing easy, we have functionality that allows us to intercept requests to the GitHub API and provide mock responses. From ccf4fad912021c2f70c6cb81a75d7579485f236c Mon Sep 17 00:00:00 2001 From: Alexander Eyers-Taylor Date: Fri, 25 Nov 2022 16:57:50 +0000 Subject: [PATCH 38/58] Hopefully make lint happy --- extensions/ql-vscode/src/extension.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 9dbdba2d2..1e1b2704b 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -1424,7 +1424,9 @@ async function activateWithInstalledDistribution( void logger.log("Starting language server."); await client.start(); ctx.subscriptions.push({ - dispose: () => { void client.stop(); } + dispose: () => { + void client.stop(); + } }); // Jump-to-definition and find-references void logger.log("Registering jump-to-definition handlers."); From a16366caaed8099d0e8d3469272ec4ce01c21779 Mon Sep 17 00:00:00 2001 From: Alexander Eyers-Taylor Date: Fri, 25 Nov 2022 17:14:41 +0000 Subject: [PATCH 39/58] More changes to make lint happy --- extensions/ql-vscode/src/extension.ts | 6 +++--- extensions/ql-vscode/src/legacy-query-server/run-queries.ts | 5 ++++- extensions/ql-vscode/src/quick-query.ts | 5 ++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 1e1b2704b..86147e11c 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -1424,9 +1424,9 @@ async function activateWithInstalledDistribution( void logger.log("Starting language server."); await client.start(); ctx.subscriptions.push({ - dispose: () => { - void client.stop(); - } + dispose: () => { + void client.stop(); + }, }); // Jump-to-definition and find-references void logger.log("Registering jump-to-definition handlers."); diff --git a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts index 4eee678eb..fe10bad44 100644 --- a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts +++ b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts @@ -480,7 +480,10 @@ export async function compileAndRunQueryAgainstDatabase( try { errors = await query.compile(qs, qlProgram, progress, token); } catch (e) { - if (e instanceof ResponseError && e.code == LSPErrorCodes.RequestCancelled) { + if ( + e instanceof ResponseError && + e.code == LSPErrorCodes.RequestCancelled + ) { return createSyntheticResult(query, "Query cancelled"); } else { throw e; diff --git a/extensions/ql-vscode/src/quick-query.ts b/extensions/ql-vscode/src/quick-query.ts index c3c98e9c9..296916a93 100644 --- a/extensions/ql-vscode/src/quick-query.ts +++ b/extensions/ql-vscode/src/quick-query.ts @@ -148,7 +148,10 @@ export async function displayQuickQuery( await Window.showTextDocument(await workspace.openTextDocument(qlFile)); } catch (e) { - if (e instanceof ResponseError && e.code == LSPErrorCodes.RequestCancelled) { + if ( + e instanceof ResponseError && + e.code == LSPErrorCodes.RequestCancelled + ) { throw new UserCancellationException(getErrorMessage(e)); } else { throw e; From 68de9f09e48b8490d68014366c4669a66e1ff902 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 25 Nov 2022 18:37:15 +0100 Subject: [PATCH 40/58] Disable fail-fast for CLI integration tests --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8741cb155..5dd83c687 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -186,6 +186,7 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] version: ['v2.7.6', 'v2.8.5', 'v2.9.4', 'v2.10.5', 'v2.11.4', 'nightly'] + fail-fast: false env: CLI_VERSION: ${{ matrix.version }} NIGHTLY_URL: ${{ needs.find-nightly.outputs.url }} From 644bea27adc5b0e051b4f782de636ae4e8941d51 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 28 Nov 2022 11:32:17 +0100 Subject: [PATCH 41/58] Add retries of CLI integration tests This will patch `jest-runner-vscode` to retry tests. This is a temporary test to see if this will help with the flakiness of the CLI integration tests. The biggest problem with this is that it will launch multiple VSCode instances on every retry: - First try (not a retry): 1 instance - Second try: 2 instances - Third try: 3 instances - etc. I'm not sure why this is happening and can't really narrow it down to a specific cause. Even if I change the `runVSCode` call for the retry by a simple `cp.spawn` call, it still launches multiple instances. --- .../patches/jest-runner-vscode+3.0.1.patch | 86 +++++++++++++++++++ .../jest-runner-vscode.config.js | 1 + 2 files changed, 87 insertions(+) diff --git a/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch b/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch index 9e01a48d2..50ef9d26f 100644 --- a/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch +++ b/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch @@ -17,3 +17,89 @@ index 0663c5c..4991663 100644 const options = JSON.parse(PARENT_JEST_OPTIONS); const jestOptions = [ ...options.args, +diff --git a/node_modules/jest-runner-vscode/dist/public-types.d.ts b/node_modules/jest-runner-vscode/dist/public-types.d.ts +index 57716e5..d8614af 100644 +--- a/node_modules/jest-runner-vscode/dist/public-types.d.ts ++++ b/node_modules/jest-runner-vscode/dist/public-types.d.ts +@@ -59,4 +59,5 @@ export interface RunnerOptions { + * code, or download progress. Defaults to `false`. + */ + quiet?: boolean; ++ retries?: number; + } +diff --git a/node_modules/jest-runner-vscode/dist/run-vscode.d.ts b/node_modules/jest-runner-vscode/dist/run-vscode.d.ts +index 8657ace..4d35409 100644 +--- a/node_modules/jest-runner-vscode/dist/run-vscode.d.ts ++++ b/node_modules/jest-runner-vscode/dist/run-vscode.d.ts +@@ -16,5 +16,7 @@ export declare type RunVSCodeOptions = { + onFailure: JestRunner.OnTestFailure; + ipc: InstanceType; + quiet?: boolean; ++ attempt?: number; ++ maxRetries?: number; + }; +-export default function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, filterOutput, onStart, onResult, onFailure, ipc, quiet, }: RunVSCodeOptions): Promise; ++export default function runVSCode(options: RunVSCodeOptions): Promise; +diff --git a/node_modules/jest-runner-vscode/dist/run-vscode.js b/node_modules/jest-runner-vscode/dist/run-vscode.js +index 5d8e513..6edf99a 100644 +--- a/node_modules/jest-runner-vscode/dist/run-vscode.js ++++ b/node_modules/jest-runner-vscode/dist/run-vscode.js +@@ -5,7 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) { + Object.defineProperty(exports, "__esModule", { value: true }); + const child_process_1 = __importDefault(require("child_process")); + const console_1 = __importDefault(require("console")); +-async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, filterOutput, onStart, onResult, onFailure, ipc, quiet, }) { ++async function runVSCode(options) { ++ const { vscodePath, args, jestArgs, env, tests, globalConfig, filterOutput, onStart, onResult, onFailure, ipc, quiet, attempt, maxRetries, } = options; + return await new Promise(resolve => { + const useStdErr = globalConfig.json || globalConfig.useStderr; + const log = useStdErr +@@ -101,11 +102,31 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, + const message = `VS Code exited with exit code ${exit}`; + if (typeof code !== 'number' || code !== 0) { + silent || quiet || console_1.default.error(message); +- const error = vscodeError ?? childError ?? new Error(message); +- for (const test of tests) { +- const completed = completedTests.has(test); +- if (!completed) { +- await onFailure(test, error); ++ const currentAttempt = attempt ?? 0; ++ if (maxRetries && maxRetries > 0 && currentAttempt < maxRetries) { ++ const newAttempt = currentAttempt + 1; ++ const newTests = tests.filter(test => !completedTests.has(test)); ++ ipc.server.off('testFileResult', onTestFileResult); ++ ipc.server.off('testStart', onTestStart); ++ ipc.server.off('testFileStart', onTestStart); ++ ipc.server.off('stdout', onStdout); ++ ipc.server.off('stderr', onStderr); ++ ipc.server.off('error', onError); ++ await runVSCode({ ++ ...options, ++ tests: newTests, ++ attempt: newAttempt, ++ }); ++ resolve(); ++ return; ++ } ++ else { ++ const error = vscodeError ?? childError ?? new Error(message); ++ for (const test of tests) { ++ const completed = completedTests.has(test); ++ if (!completed) { ++ await onFailure(test, error); ++ } + } + } + } +diff --git a/node_modules/jest-runner-vscode/dist/runner.js b/node_modules/jest-runner-vscode/dist/runner.js +index e24c976..c374022 100644 +--- a/node_modules/jest-runner-vscode/dist/runner.js ++++ b/node_modules/jest-runner-vscode/dist/runner.js +@@ -107,6 +107,7 @@ class VSCodeTestRunner { + onFailure, + ipc, + quiet: vscodeOptions.quiet, ++ maxRetries: vscodeOptions.retries, + }); + } + catch (error) { diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.js b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.js index ec56deb64..8b2e60fd4 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.js +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/jest-runner-vscode.config.js @@ -25,6 +25,7 @@ const config = { ...baseConfig.extensionTestsEnv, INTEGRATION_TEST_MODE: "true", }, + retries: 3, }; module.exports = config; From 9a9a8c5ac4c97f2bfe469ae9109fafa3e6492d64 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 28 Nov 2022 11:51:23 +0100 Subject: [PATCH 42/58] Fix multiple VSCode instances launching Multiple VSCode instances were being launched when a second instance of VSCode was being spawned with the same user data directory. This is probably because VSCode restores the windows from the previous session, even when `-n`/`--new-window` is passed. This fixes it by patching `jest-runner-vscode` to always create a new temporary user data directory, rather than re-using the same one for all test suites. --- .../patches/jest-runner-vscode+3.0.1.patch | 39 +++++++++++++++++-- .../jest-runner-vscode.config.base.js | 1 - 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch b/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch index 50ef9d26f..8de830419 100644 --- a/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch +++ b/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch @@ -41,20 +41,44 @@ index 8657ace..4d35409 100644 -export default function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, filterOutput, onStart, onResult, onFailure, ipc, quiet, }: RunVSCodeOptions): Promise; +export default function runVSCode(options: RunVSCodeOptions): Promise; diff --git a/node_modules/jest-runner-vscode/dist/run-vscode.js b/node_modules/jest-runner-vscode/dist/run-vscode.js -index 5d8e513..6edf99a 100644 +index 5d8e513..cacbc42 100644 --- a/node_modules/jest-runner-vscode/dist/run-vscode.js +++ b/node_modules/jest-runner-vscode/dist/run-vscode.js -@@ -5,7 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) { +@@ -5,8 +5,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", { value: true }); const child_process_1 = __importDefault(require("child_process")); const console_1 = __importDefault(require("console")); -async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, filterOutput, onStart, onResult, onFailure, ipc, quiet, }) { +- return await new Promise(resolve => { ++const fs_1 = __importDefault(require("fs")); ++const path_1 = __importDefault(require("path")); ++const os_1 = __importDefault(require("os")); +async function runVSCode(options) { + const { vscodePath, args, jestArgs, env, tests, globalConfig, filterOutput, onStart, onResult, onFailure, ipc, quiet, attempt, maxRetries, } = options; - return await new Promise(resolve => { ++ const tempUserDir = await fs_1.default.promises.mkdtemp(path_1.default.resolve(os_1.default.tmpdir(), 'jest-runner-vscode-user-data-')); ++ return await new Promise(promiseResolve => { ++ const resolve = () => { ++ fs_1.default.rm(tempUserDir, { recursive: true }, () => { ++ promiseResolve(); ++ }); ++ }; const useStdErr = globalConfig.json || globalConfig.useStderr; const log = useStdErr -@@ -101,11 +102,31 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, + ? console_1.default.error.bind(console_1.default) +@@ -82,7 +92,11 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, + ipc.server.on('stdout', onStdout); + ipc.server.on('stderr', onStderr); + ipc.server.on('error', onError); +- const vscode = child_process_1.default.spawn(vscodePath, args, { env: environment }); ++ const launchArgs = args; ++ if (!hasArg('user-data-dir', launchArgs)) { ++ launchArgs.push(`--user-data-dir=${tempUserDir}`); ++ } ++ const vscode = child_process_1.default.spawn(vscodePath, launchArgs, { env: environment }); + if (!silent && !filterOutput) { + vscode.stdout.pipe(process.stdout); + vscode.stderr.pipe(process.stderr); +@@ -101,11 +115,31 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, const message = `VS Code exited with exit code ${exit}`; if (typeof code !== 'number' || code !== 0) { silent || quiet || console_1.default.error(message); @@ -91,6 +115,13 @@ index 5d8e513..6edf99a 100644 } } } +@@ -138,3 +172,6 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, + }); + } + exports.default = runVSCode; ++function hasArg(argName, argList) { ++ return argList.some(a => a === `--${argName}` || a.startsWith(`--${argName}=`)); ++} diff --git a/node_modules/jest-runner-vscode/dist/runner.js b/node_modules/jest-runner-vscode/dist/runner.js index e24c976..c374022 100644 --- a/node_modules/jest-runner-vscode/dist/runner.js diff --git a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.js b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.js index f8e1a45cb..24f6a74eb 100644 --- a/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.js +++ b/extensions/ql-vscode/src/vscode-tests/jest-runner-vscode.config.base.js @@ -11,7 +11,6 @@ const config = { launchArgs: [ "--disable-gpu", "--extensions-dir=" + path.join(rootDir, ".vscode-test", "extensions"), - "--user-data-dir=" + path.join(tmpDir.name, "user-data"), ], extensionDevelopmentPath: rootDir, }; From 7ba90275a8c71f7e06142035f07cc762af7765fb Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 28 Nov 2022 13:05:13 +0100 Subject: [PATCH 43/58] Increase scenarios in which retries will be used This will update the `jest-runner-vscode` patch to retry tests that fail due to no test result being returned from the test runner. This will also add some retries to the `minimal-workspace` and `no-workspace` tests to help with flakiness. --- .../patches/jest-runner-vscode+3.0.1.patch | 65 +++++++++---------- .../jest-runner-vscode.config.js | 1 + .../no-workspace/jest-runner-vscode.config.js | 1 + 3 files changed, 31 insertions(+), 36 deletions(-) diff --git a/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch b/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch index 8de830419..833ba0428 100644 --- a/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch +++ b/extensions/ql-vscode/patches/jest-runner-vscode+3.0.1.patch @@ -41,7 +41,7 @@ index 8657ace..4d35409 100644 -export default function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, filterOutput, onStart, onResult, onFailure, ipc, quiet, }: RunVSCodeOptions): Promise; +export default function runVSCode(options: RunVSCodeOptions): Promise; diff --git a/node_modules/jest-runner-vscode/dist/run-vscode.js b/node_modules/jest-runner-vscode/dist/run-vscode.js -index 5d8e513..cacbc42 100644 +index 5d8e513..7e556ee 100644 --- a/node_modules/jest-runner-vscode/dist/run-vscode.js +++ b/node_modules/jest-runner-vscode/dist/run-vscode.js @@ -5,8 +5,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) { @@ -78,44 +78,37 @@ index 5d8e513..cacbc42 100644 if (!silent && !filterOutput) { vscode.stdout.pipe(process.stdout); vscode.stderr.pipe(process.stderr); -@@ -101,11 +115,31 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, +@@ -99,6 +113,29 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, + exited = true; + const exit = code ?? signal ?? ''; const message = `VS Code exited with exit code ${exit}`; ++ const currentAttempt = attempt ?? 0; ++ const incompleteTests = tests.some(test => !completedTests.has(test)); ++ if (maxRetries && ++ maxRetries > 0 && ++ currentAttempt < maxRetries && ++ incompleteTests) { ++ silent || quiet || log(message); ++ const newAttempt = currentAttempt + 1; ++ const newTests = tests.filter(test => !completedTests.has(test)); ++ ipc.server.off('testFileResult', onTestFileResult); ++ ipc.server.off('testStart', onTestStart); ++ ipc.server.off('testFileStart', onTestStart); ++ ipc.server.off('stdout', onStdout); ++ ipc.server.off('stderr', onStderr); ++ ipc.server.off('error', onError); ++ await runVSCode({ ++ ...options, ++ tests: newTests, ++ attempt: newAttempt, ++ }); ++ resolve(); ++ return; ++ } if (typeof code !== 'number' || code !== 0) { silent || quiet || console_1.default.error(message); -- const error = vscodeError ?? childError ?? new Error(message); -- for (const test of tests) { -- const completed = completedTests.has(test); -- if (!completed) { -- await onFailure(test, error); -+ const currentAttempt = attempt ?? 0; -+ if (maxRetries && maxRetries > 0 && currentAttempt < maxRetries) { -+ const newAttempt = currentAttempt + 1; -+ const newTests = tests.filter(test => !completedTests.has(test)); -+ ipc.server.off('testFileResult', onTestFileResult); -+ ipc.server.off('testStart', onTestStart); -+ ipc.server.off('testFileStart', onTestStart); -+ ipc.server.off('stdout', onStdout); -+ ipc.server.off('stderr', onStderr); -+ ipc.server.off('error', onError); -+ await runVSCode({ -+ ...options, -+ tests: newTests, -+ attempt: newAttempt, -+ }); -+ resolve(); -+ return; -+ } -+ else { -+ const error = vscodeError ?? childError ?? new Error(message); -+ for (const test of tests) { -+ const completed = completedTests.has(test); -+ if (!completed) { -+ await onFailure(test, error); -+ } - } - } - } -@@ -138,3 +172,6 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, + const error = vscodeError ?? childError ?? new Error(message); +@@ -138,3 +175,6 @@ async function runVSCode({ vscodePath, args, jestArgs, env, tests, globalConfig, }); } exports.default = runVSCode; diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.js b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.js index 027ec79b2..5bdcefc49 100644 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.js +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/jest-runner-vscode.config.js @@ -13,6 +13,7 @@ const config = { "--disable-extensions", path.resolve(rootDir, "test/data"), ], + retries: 1, }; module.exports = config; diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.js b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.js index d41dccb07..8db7a18f6 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.js +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/jest-runner-vscode.config.js @@ -4,6 +4,7 @@ const { config: baseConfig } = require("../jest-runner-vscode.config.base"); const config = { ...baseConfig, launchArgs: [...(baseConfig.launchArgs ?? []), "--disable-extensions"], + retries: 1, }; module.exports = config; From bc65d8f31164cf6bb6e2de97450b42b99ac568a2 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 29 Nov 2022 14:26:36 +0100 Subject: [PATCH 44/58] Fix loading results in second opened view When results were already cached in memory and the view requested the result, it would not be loaded because the event would not be fired. This fires the event when the result is loaded from cache as well, to ensure that the view always receives the result. --- .../src/remote-queries/variant-analysis-results-manager.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-results-manager.ts b/extensions/ql-vscode/src/remote-queries/variant-analysis-results-manager.ts index 9ba8ccf96..00f245597 100644 --- a/extensions/ql-vscode/src/remote-queries/variant-analysis-results-manager.ts +++ b/extensions/ql-vscode/src/remote-queries/variant-analysis-results-manager.ts @@ -121,6 +121,7 @@ export class VariantAnalysisResultsManager extends DisposableObject { createCacheKey(variantAnalysisId, repositoryFullName), ); if (result) { + this._onResultLoaded.fire(result); return result; } From 75b684b00f9f0538f1cb2bcb33fa49f5252a088e Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Tue, 29 Nov 2022 14:37:19 +0000 Subject: [PATCH 45/58] MRVA: Fix typo in repo cancellation message (#1807) --- .../src/view/variant-analysis/AnalyzedRepoItemContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/view/variant-analysis/AnalyzedRepoItemContent.tsx b/extensions/ql-vscode/src/view/variant-analysis/AnalyzedRepoItemContent.tsx index 91c679aee..085cbd84d 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/AnalyzedRepoItemContent.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/AnalyzedRepoItemContent.tsx @@ -76,7 +76,7 @@ export const AnalyzedRepoItemContent = ({ )} From 339819c82b2fd30a33a0151a59b94cb6bcba591e Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 29 Nov 2022 14:56:09 +0000 Subject: [PATCH 46/58] Don't show pending state when analysis has failed --- .../src/view/variant-analysis/VariantAnalysis.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx index b76e6a316..02b4f8957 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx @@ -5,6 +5,7 @@ import { VariantAnalysis as VariantAnalysisDomainModel, VariantAnalysisScannedRepositoryResult, VariantAnalysisScannedRepositoryState, + VariantAnalysisStatus, } from "../../remote-queries/shared/variant-analysis"; import { VariantAnalysisHeader } from "./VariantAnalysisHeader"; import { VariantAnalysisOutcomePanels } from "./VariantAnalysisOutcomePanels"; @@ -128,7 +129,11 @@ export function VariantAnalysis({ }); }, [filterSortState, selectedRepositoryIds]); - if (variantAnalysis?.actionsWorkflowRunId === undefined) { + if ( + variantAnalysis === undefined || + (variantAnalysis.status === VariantAnalysisStatus.InProgress && + variantAnalysis.actionsWorkflowRunId === undefined) + ) { return ; } From 2cdd6de38ecc59bcda94768374560449b9143c32 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 29 Nov 2022 15:00:10 +0000 Subject: [PATCH 47/58] Don't show spinner for a failed analysis --- .../src/view/variant-analysis/VariantAnalysis.tsx | 5 ++++- .../variant-analysis/VariantAnalysisHeader.tsx | 2 +- .../variant-analysis/VariantAnalysisStats.tsx | 3 ++- .../VariantAnalysisStatusStats.tsx | 15 ++++++++++----- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx index b76e6a316..a67d87420 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx @@ -132,6 +132,9 @@ export function VariantAnalysis({ return ; } + const onViewLogsClick = + variantAnalysis.actionsWorkflowRunId === undefined ? undefined : openLogs; + return ( <> void; onExportResultsClick: () => void; - onViewLogsClick: () => void; + onViewLogsClick?: () => void; }; const Container = styled.div` diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx index 5a56e1c94..f0883de7d 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx @@ -20,7 +20,7 @@ export type VariantAnalysisStatsProps = { createdAt: Date; completedAt?: Date | undefined; - onViewLogsClick: () => void; + onViewLogsClick?: () => void; }; const Row = styled.div` @@ -88,6 +88,7 @@ export const VariantAnalysisStats = ({ diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx index 9f37ba8cc..675a49bc1 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx @@ -2,11 +2,13 @@ import * as React from "react"; import styled from "styled-components"; import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"; import { formatDate } from "../../pure/date"; +import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis"; type Props = { - completedAt?: Date | undefined; + variantAnalysisStatus: VariantAnalysisStatus; + completedAt?: Date; - onViewLogsClick: () => void; + onViewLogsClick?: () => void; }; const Container = styled.div` @@ -21,17 +23,20 @@ const Icon = styled.span` `; export const VariantAnalysisStatusStats = ({ + variantAnalysisStatus, completedAt, onViewLogsClick, }: Props) => { - if (completedAt === undefined) { + if (variantAnalysisStatus === VariantAnalysisStatus.InProgress) { return ; } return ( - {formatDate(completedAt)} - View logs + {completedAt !== undefined ? formatDate(completedAt) : "-"} + {onViewLogsClick && ( + View logs + )} ); }; From 3fdc3287675bbb79a2598df60712b3e44072ff4d Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 29 Nov 2022 16:49:04 +0100 Subject: [PATCH 48/58] Add additional Storybook VSCode themes This adds four new VSCode themes to Storybook which will allow us to more easily test these themes in Storybook. These themes were chosen because they are either used for accessibility (the high contrast themes) or are currently not compatible with the variant analysis UI (there are items that are not visible). --- .../.storybook/vscode-theme-addon/theme.ts | 8 + .../vscode-theme-addon/withTheme.ts | 16 + .../vscode-theme-dark-high-contrast.css | 577 +++++++++++++++ .../vscode-theme-github-dark-default.css | 674 ++++++++++++++++++ .../vscode-theme-github-light-default.css | 659 +++++++++++++++++ .../vscode-theme-light-high-contrast.css | 596 ++++++++++++++++ 6 files changed, 2530 insertions(+) create mode 100644 extensions/ql-vscode/src/stories/vscode-theme-dark-high-contrast.css create mode 100644 extensions/ql-vscode/src/stories/vscode-theme-github-dark-default.css create mode 100644 extensions/ql-vscode/src/stories/vscode-theme-github-light-default.css create mode 100644 extensions/ql-vscode/src/stories/vscode-theme-light-high-contrast.css diff --git a/extensions/ql-vscode/.storybook/vscode-theme-addon/theme.ts b/extensions/ql-vscode/.storybook/vscode-theme-addon/theme.ts index 0aee11d94..2631b92e5 100644 --- a/extensions/ql-vscode/.storybook/vscode-theme-addon/theme.ts +++ b/extensions/ql-vscode/.storybook/vscode-theme-addon/theme.ts @@ -1,9 +1,17 @@ export enum VSCodeTheme { Dark = "dark", Light = "light", + LightHighContrast = "light-high-contrast", + DarkHighContrast = "dark-high-contrast", + GitHubLightDefault = "github-light-default", + GitHubDarkDefault = "github-dark-default", } export const themeNames: { [key in VSCodeTheme]: string } = { [VSCodeTheme.Dark]: "Dark+", [VSCodeTheme.Light]: "Light+", + [VSCodeTheme.LightHighContrast]: "Light High Contrast", + [VSCodeTheme.DarkHighContrast]: "Dark High Contrast", + [VSCodeTheme.GitHubLightDefault]: "GitHub Light Default", + [VSCodeTheme.GitHubDarkDefault]: "GitHub Dark Default", }; diff --git a/extensions/ql-vscode/.storybook/vscode-theme-addon/withTheme.ts b/extensions/ql-vscode/.storybook/vscode-theme-addon/withTheme.ts index 0f9cf316c..4e30549cc 100644 --- a/extensions/ql-vscode/.storybook/vscode-theme-addon/withTheme.ts +++ b/extensions/ql-vscode/.storybook/vscode-theme-addon/withTheme.ts @@ -16,6 +16,22 @@ const themeFiles: { [key in VSCodeTheme]: string } = { // eslint-disable-next-line @typescript-eslint/no-var-requires require("!file-loader?modules!../../src/stories/vscode-theme-light.css") .default, + [VSCodeTheme.LightHighContrast]: + // eslint-disable-next-line @typescript-eslint/no-var-requires + require("!file-loader?modules!../../src/stories/vscode-theme-light-high-contrast.css") + .default, + [VSCodeTheme.DarkHighContrast]: + // eslint-disable-next-line @typescript-eslint/no-var-requires + require("!file-loader?modules!../../src/stories/vscode-theme-dark-high-contrast.css") + .default, + [VSCodeTheme.GitHubLightDefault]: + // eslint-disable-next-line @typescript-eslint/no-var-requires + require("!file-loader?modules!../../src/stories/vscode-theme-github-light-default.css") + .default, + [VSCodeTheme.GitHubDarkDefault]: + // eslint-disable-next-line @typescript-eslint/no-var-requires + require("!file-loader?modules!../../src/stories/vscode-theme-github-dark-default.css") + .default, }; export const withTheme = ( diff --git a/extensions/ql-vscode/src/stories/vscode-theme-dark-high-contrast.css b/extensions/ql-vscode/src/stories/vscode-theme-dark-high-contrast.css new file mode 100644 index 000000000..1193819d4 --- /dev/null +++ b/extensions/ql-vscode/src/stories/vscode-theme-dark-high-contrast.css @@ -0,0 +1,577 @@ +/* + * These were copied from VSCode Dark High Contrast theme. + * + * To update these, open a webview in VSCode, open the webview developer tools and find the + * iframe hosting the webview. The element will have a style attribute that contains + * the CSS variables. Copy these to this file. + */ +:root { + --vscode-font-family: -apple-system, BlinkMacSystemFont, sans-serif; + --vscode-font-weight: normal; + --vscode-font-size: 13px; + --vscode-editor-font-family: Menlo, Monaco, "Courier New", monospace; + --vscode-editor-font-weight: normal; + --vscode-editor-font-size: 12px; + --vscode-foreground: #ffffff; + --vscode-disabledForeground: #a5a5a5; + --vscode-errorForeground: #f48771; + --vscode-descriptionForeground: rgba(255, 255, 255, 0.7); + --vscode-icon-foreground: #ffffff; + --vscode-focusBorder: #f38518; + --vscode-contrastBorder: #6fc3df; + --vscode-contrastActiveBorder: #f38518; + --vscode-selection-background: #008000; + --vscode-textSeparator-foreground: #000000; + --vscode-textLink-foreground: #3794ff; + --vscode-textLink-activeForeground: #3794ff; + --vscode-textPreformat-foreground: #d7ba7d; + --vscode-textBlockQuote-border: #ffffff; + --vscode-textCodeBlock-background: #000000; + --vscode-input-background: #000000; + --vscode-input-foreground: #ffffff; + --vscode-input-border: #6fc3df; + --vscode-inputOption-activeBorder: #6fc3df; + --vscode-inputOption-activeBackground: rgba(0, 0, 0, 0); + --vscode-inputOption-activeForeground: #ffffff; + --vscode-input-placeholderForeground: rgba(255, 255, 255, 0.7); + --vscode-inputValidation-infoBackground: #000000; + --vscode-inputValidation-infoBorder: #6fc3df; + --vscode-inputValidation-warningBackground: #000000; + --vscode-inputValidation-warningBorder: #6fc3df; + --vscode-inputValidation-errorBackground: #000000; + --vscode-inputValidation-errorBorder: #6fc3df; + --vscode-dropdown-background: #000000; + --vscode-dropdown-listBackground: #000000; + --vscode-dropdown-foreground: #ffffff; + --vscode-dropdown-border: #6fc3df; + --vscode-button-foreground: #ffffff; + --vscode-button-separator: rgba(255, 255, 255, 0.4); + --vscode-button-border: #6fc3df; + --vscode-button-secondaryForeground: #ffffff; + --vscode-badge-background: #000000; + --vscode-badge-foreground: #ffffff; + --vscode-scrollbarSlider-background: rgba(111, 195, 223, 0.6); + --vscode-scrollbarSlider-hoverBackground: rgba(111, 195, 223, 0.8); + --vscode-scrollbarSlider-activeBackground: #6fc3df; + --vscode-progressBar-background: #6fc3df; + --vscode-editorError-foreground: #f48771; + --vscode-editorError-border: rgba(228, 119, 119, 0.8); + --vscode-editorWarning-foreground: #ff0000; + --vscode-editorWarning-border: rgba(255, 204, 0, 0.8); + --vscode-editorInfo-foreground: #3794ff; + --vscode-editorInfo-border: rgba(55, 148, 255, 0.8); + --vscode-editorHint-border: rgba(238, 238, 238, 0.8); + --vscode-sash-hoverBorder: #f38518; + --vscode-editor-background: #000000; + --vscode-editor-foreground: #ffffff; + --vscode-editorStickyScroll-background: #000000; + --vscode-editorWidget-background: #0c141f; + --vscode-editorWidget-foreground: #ffffff; + --vscode-editorWidget-border: #6fc3df; + --vscode-quickInput-background: #0c141f; + --vscode-quickInput-foreground: #ffffff; + --vscode-quickInputTitle-background: #000000; + --vscode-pickerGroup-foreground: #ffffff; + --vscode-pickerGroup-border: #ffffff; + --vscode-keybindingLabel-background: rgba(0, 0, 0, 0); + --vscode-keybindingLabel-foreground: #ffffff; + --vscode-keybindingLabel-border: #6fc3df; + --vscode-keybindingLabel-bottomBorder: #6fc3df; + --vscode-editor-selectionBackground: #ffffff; + --vscode-editor-selectionForeground: #000000; + --vscode-editor-inactiveSelectionBackground: rgba(255, 255, 255, 0.7); + --vscode-editor-selectionHighlightBorder: #f38518; + --vscode-editor-findMatchBorder: #f38518; + --vscode-editor-findMatchHighlightBorder: #f38518; + --vscode-editor-findRangeHighlightBorder: rgba(243, 133, 24, 0.4); + --vscode-searchEditor-findMatchBorder: #f38518; + --vscode-editor-hoverHighlightBackground: rgba(173, 214, 255, 0.15); + --vscode-editorHoverWidget-background: #0c141f; + --vscode-editorHoverWidget-foreground: #ffffff; + --vscode-editorHoverWidget-border: #6fc3df; + --vscode-editorHoverWidget-statusBarBackground: #0c141f; + --vscode-editorLink-activeForeground: #00ffff; + --vscode-editorInlayHint-foreground: #000000; + --vscode-editorInlayHint-background: #f38518; + --vscode-editorInlayHint-typeForeground: #000000; + --vscode-editorInlayHint-typeBackground: #f38518; + --vscode-editorInlayHint-parameterForeground: #000000; + --vscode-editorInlayHint-parameterBackground: #f38518; + --vscode-editorLightBulb-foreground: #ffcc00; + --vscode-editorLightBulbAutoFix-foreground: #75beff; + --vscode-diffEditor-insertedTextBorder: #33ff2e; + --vscode-diffEditor-removedTextBorder: #ff008f; + --vscode-diffEditor-border: #6fc3df; + --vscode-list-focusOutline: #f38518; + --vscode-list-highlightForeground: #f38518; + --vscode-list-focusHighlightForeground: #f38518; + --vscode-list-invalidItemForeground: #b89500; + --vscode-listFilterWidget-background: #0c141f; + --vscode-listFilterWidget-outline: #f38518; + --vscode-listFilterWidget-noMatchesOutline: #6fc3df; + --vscode-list-filterMatchBorder: #6fc3df; + --vscode-tree-indentGuidesStroke: #a9a9a9; + --vscode-list-deemphasizedForeground: #a7a8a9; + --vscode-checkbox-background: #000000; + --vscode-checkbox-selectBackground: #0c141f; + --vscode-checkbox-foreground: #ffffff; + --vscode-checkbox-border: #6fc3df; + --vscode-checkbox-selectBorder: #0c141f; + --vscode-menu-border: #6fc3df; + --vscode-menu-foreground: #ffffff; + --vscode-menu-background: #000000; + --vscode-menu-selectionBorder: #f38518; + --vscode-menu-separatorBackground: #6fc3df; + --vscode-toolbar-hoverOutline: #f38518; + --vscode-editor-snippetTabstopHighlightBackground: rgba(124, 124, 124, 0.3); + --vscode-editor-snippetFinalTabstopHighlightBorder: #525252; + --vscode-breadcrumb-foreground: rgba(255, 255, 255, 0.8); + --vscode-breadcrumb-background: #000000; + --vscode-breadcrumb-focusForeground: #ffffff; + --vscode-breadcrumb-activeSelectionForeground: #ffffff; + --vscode-breadcrumbPicker-background: #0c141f; + --vscode-merge-border: #c3df6f; + --vscode-editorOverviewRuler-currentContentForeground: #c3df6f; + --vscode-editorOverviewRuler-incomingContentForeground: #c3df6f; + --vscode-editorOverviewRuler-commonContentForeground: #c3df6f; + --vscode-editorOverviewRuler-findMatchForeground: #ab5a00; + --vscode-editorOverviewRuler-selectionHighlightForeground: rgba( + 160, + 160, + 160, + 0.8 + ); + --vscode-minimap-findMatchHighlight: #ab5a00; + --vscode-minimap-selectionOccurrenceHighlight: #ffffff; + --vscode-minimap-selectionHighlight: #ffffff; + --vscode-minimap-errorHighlight: #ff3232; + --vscode-minimap-warningHighlight: rgba(255, 204, 0, 0.8); + --vscode-minimap-foregroundOpacity: #000000; + --vscode-minimapSlider-background: rgba(111, 195, 223, 0.3); + --vscode-minimapSlider-hoverBackground: rgba(111, 195, 223, 0.4); + --vscode-minimapSlider-activeBackground: rgba(111, 195, 223, 0.5); + --vscode-problemsErrorIcon-foreground: #f48771; + --vscode-problemsWarningIcon-foreground: #ff0000; + --vscode-problemsInfoIcon-foreground: #3794ff; + --vscode-charts-foreground: #ffffff; + --vscode-charts-lines: rgba(255, 255, 255, 0.5); + --vscode-charts-red: #f48771; + --vscode-charts-blue: #3794ff; + --vscode-charts-yellow: #ff0000; + --vscode-charts-orange: #ab5a00; + --vscode-charts-green: #89d185; + --vscode-charts-purple: #b180d7; + --vscode-symbolIcon-arrayForeground: #ffffff; + --vscode-symbolIcon-booleanForeground: #ffffff; + --vscode-symbolIcon-classForeground: #ee9d28; + --vscode-symbolIcon-colorForeground: #ffffff; + --vscode-symbolIcon-constantForeground: #ffffff; + --vscode-symbolIcon-constructorForeground: #b180d7; + --vscode-symbolIcon-enumeratorForeground: #ee9d28; + --vscode-symbolIcon-enumeratorMemberForeground: #75beff; + --vscode-symbolIcon-eventForeground: #ee9d28; + --vscode-symbolIcon-fieldForeground: #75beff; + --vscode-symbolIcon-fileForeground: #ffffff; + --vscode-symbolIcon-folderForeground: #ffffff; + --vscode-symbolIcon-functionForeground: #b180d7; + --vscode-symbolIcon-interfaceForeground: #75beff; + --vscode-symbolIcon-keyForeground: #ffffff; + --vscode-symbolIcon-keywordForeground: #ffffff; + --vscode-symbolIcon-methodForeground: #b180d7; + --vscode-symbolIcon-moduleForeground: #ffffff; + --vscode-symbolIcon-namespaceForeground: #ffffff; + --vscode-symbolIcon-nullForeground: #ffffff; + --vscode-symbolIcon-numberForeground: #ffffff; + --vscode-symbolIcon-objectForeground: #ffffff; + --vscode-symbolIcon-operatorForeground: #ffffff; + --vscode-symbolIcon-packageForeground: #ffffff; + --vscode-symbolIcon-propertyForeground: #ffffff; + --vscode-symbolIcon-referenceForeground: #ffffff; + --vscode-symbolIcon-snippetForeground: #ffffff; + --vscode-symbolIcon-stringForeground: #ffffff; + --vscode-symbolIcon-structForeground: #ffffff; + --vscode-symbolIcon-textForeground: #ffffff; + --vscode-symbolIcon-typeParameterForeground: #ffffff; + --vscode-symbolIcon-unitForeground: #ffffff; + --vscode-symbolIcon-variableForeground: #75beff; + --vscode-editor-lineHighlightBorder: #f38518; + --vscode-editor-rangeHighlightBorder: #f38518; + --vscode-editor-symbolHighlightBorder: #f38518; + --vscode-editorCursor-foreground: #ffffff; + --vscode-editorWhitespace-foreground: #7c7c7c; + --vscode-editorIndentGuide-background: #ffffff; + --vscode-editorIndentGuide-activeBackground: #ffffff; + --vscode-editorLineNumber-foreground: #ffffff; + --vscode-editorActiveLineNumber-foreground: #f38518; + --vscode-editorLineNumber-activeForeground: #f38518; + --vscode-editorRuler-foreground: #ffffff; + --vscode-editorCodeLens-foreground: #999999; + --vscode-editorBracketMatch-background: rgba(0, 100, 0, 0.1); + --vscode-editorBracketMatch-border: #6fc3df; + --vscode-editorOverviewRuler-border: rgba(127, 127, 127, 0.3); + --vscode-editorGutter-background: #000000; + --vscode-editorUnnecessaryCode-border: rgba(255, 255, 255, 0.8); + --vscode-editorGhostText-border: rgba(255, 255, 255, 0.8); + --vscode-editorOverviewRuler-rangeHighlightForeground: rgba(0, 122, 204, 0.6); + --vscode-editorOverviewRuler-errorForeground: #ff3232; + --vscode-editorOverviewRuler-warningForeground: rgba(255, 204, 0, 0.8); + --vscode-editorOverviewRuler-infoForeground: rgba(55, 148, 255, 0.8); + --vscode-editorBracketHighlight-foreground1: #ffd700; + --vscode-editorBracketHighlight-foreground2: #da70d6; + --vscode-editorBracketHighlight-foreground3: #87cefa; + --vscode-editorBracketHighlight-foreground4: rgba(0, 0, 0, 0); + --vscode-editorBracketHighlight-foreground5: rgba(0, 0, 0, 0); + --vscode-editorBracketHighlight-foreground6: rgba(0, 0, 0, 0); + --vscode-editorBracketHighlight-unexpectedBracket\.foreground: #ff3232; + --vscode-editorBracketPairGuide-background1: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background2: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background3: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background4: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background5: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background6: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground1: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground2: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground3: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground4: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground5: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground6: rgba(0, 0, 0, 0); + --vscode-editorUnicodeHighlight-border: #ff0000; + --vscode-editorUnicodeHighlight-background: rgba(0, 0, 0, 0); + --vscode-editorHoverWidget-highlightForeground: #f38518; + --vscode-editorOverviewRuler-bracketMatchForeground: #a0a0a0; + --vscode-editorGutter-foldingControlForeground: #ffffff; + --vscode-editor-linkedEditingBackground: rgba(255, 0, 0, 0.3); + --vscode-editor-wordHighlightBorder: #f38518; + --vscode-editor-wordHighlightStrongBorder: #f38518; + --vscode-editorOverviewRuler-wordHighlightForeground: rgba( + 160, + 160, + 160, + 0.8 + ); + --vscode-editorOverviewRuler-wordHighlightStrongForeground: rgba( + 192, + 160, + 192, + 0.8 + ); + --vscode-peekViewTitleLabel-foreground: #ffffff; + --vscode-peekViewTitleDescription-foreground: rgba(255, 255, 255, 0.6); + --vscode-peekView-border: #6fc3df; + --vscode-peekViewResult-background: #000000; + --vscode-peekViewResult-lineForeground: #ffffff; + --vscode-peekViewResult-fileForeground: #ffffff; + --vscode-peekViewResult-selectionForeground: #ffffff; + --vscode-peekViewEditor-background: #000000; + --vscode-peekViewEditorGutter-background: #000000; + --vscode-peekViewEditor-matchHighlightBorder: #f38518; + --vscode-editorMarkerNavigationError-background: #6fc3df; + --vscode-editorMarkerNavigationWarning-background: #6fc3df; + --vscode-editorMarkerNavigationWarning-headerBackground: #0c141f; + --vscode-editorMarkerNavigationInfo-background: #6fc3df; + --vscode-editorMarkerNavigation-background: #000000; + --vscode-editorSuggestWidget-background: #0c141f; + --vscode-editorSuggestWidget-border: #6fc3df; + --vscode-editorSuggestWidget-foreground: #ffffff; + --vscode-editorSuggestWidget-highlightForeground: #f38518; + --vscode-editorSuggestWidget-focusHighlightForeground: #f38518; + --vscode-editorSuggestWidgetStatus-foreground: rgba(255, 255, 255, 0.5); + --vscode-tab-activeBackground: #000000; + --vscode-tab-unfocusedActiveBackground: #000000; + --vscode-tab-activeForeground: #ffffff; + --vscode-tab-inactiveForeground: #ffffff; + --vscode-tab-unfocusedActiveForeground: #ffffff; + --vscode-tab-unfocusedInactiveForeground: #ffffff; + --vscode-tab-border: #6fc3df; + --vscode-tab-lastPinnedBorder: #6fc3df; + --vscode-tab-inactiveModifiedBorder: #ffffff; + --vscode-tab-unfocusedActiveModifiedBorder: #ffffff; + --vscode-tab-unfocusedInactiveModifiedBorder: #ffffff; + --vscode-editorPane-background: #000000; + --vscode-editorGroup-focusedEmptyBorder: #f38518; + --vscode-editorGroupHeader-noTabsBackground: #000000; + --vscode-editorGroupHeader-border: #6fc3df; + --vscode-editorGroup-border: #6fc3df; + --vscode-editorGroup-dropIntoPromptForeground: #ffffff; + --vscode-editorGroup-dropIntoPromptBackground: #0c141f; + --vscode-editorGroup-dropIntoPromptBorder: #6fc3df; + --vscode-sideBySideEditor-horizontalBorder: #6fc3df; + --vscode-sideBySideEditor-verticalBorder: #6fc3df; + --vscode-panel-background: #000000; + --vscode-panel-border: #6fc3df; + --vscode-panelTitle-activeForeground: #ffffff; + --vscode-panelTitle-inactiveForeground: #ffffff; + --vscode-panelTitle-activeBorder: #6fc3df; + --vscode-panelInput-border: #6fc3df; + --vscode-panel-dropBorder: #ffffff; + --vscode-panelSectionHeader-border: #6fc3df; + --vscode-panelSection-border: #6fc3df; + --vscode-banner-iconForeground: #3794ff; + --vscode-statusBar-foreground: #ffffff; + --vscode-statusBar-noFolderForeground: #ffffff; + --vscode-statusBar-border: #6fc3df; + --vscode-statusBar-noFolderBorder: #6fc3df; + --vscode-statusBarItem-activeBackground: rgba(255, 255, 255, 0.18); + --vscode-statusBarItem-hoverBackground: rgba(255, 255, 255, 0.12); + --vscode-statusBarItem-compactHoverBackground: rgba(255, 255, 255, 0.2); + --vscode-statusBarItem-prominentForeground: #ffffff; + --vscode-statusBarItem-prominentBackground: rgba(0, 0, 0, 0.5); + --vscode-statusBarItem-prominentHoverBackground: rgba(0, 0, 0, 0.3); + --vscode-statusBarItem-errorForeground: #ffffff; + --vscode-statusBarItem-warningForeground: #ffffff; + --vscode-activityBar-background: #000000; + --vscode-activityBar-foreground: #ffffff; + --vscode-activityBar-inactiveForeground: #ffffff; + --vscode-activityBar-border: #6fc3df; + --vscode-activityBarBadge-background: #000000; + --vscode-activityBarBadge-foreground: #ffffff; + --vscode-activityBarItem-profilesForeground: #ffffff; + --vscode-activityBarItem-profilesHoverForeground: #ffffff; + --vscode-statusBarItem-remoteBackground: rgba(0, 0, 0, 0); + --vscode-statusBarItem-remoteForeground: #ffffff; + --vscode-extensionBadge-remoteBackground: #000000; + --vscode-extensionBadge-remoteForeground: #ffffff; + --vscode-sideBar-background: #000000; + --vscode-sideBar-border: #6fc3df; + --vscode-sideBarTitle-foreground: #ffffff; + --vscode-sideBarSectionHeader-border: #6fc3df; + --vscode-titleBar-activeForeground: #ffffff; + --vscode-titleBar-activeBackground: #000000; + --vscode-titleBar-border: #6fc3df; + --vscode-menubar-selectionForeground: #ffffff; + --vscode-menubar-selectionBorder: #f38518; + --vscode-notificationCenter-border: #6fc3df; + --vscode-notificationToast-border: #6fc3df; + --vscode-notifications-foreground: #ffffff; + --vscode-notifications-background: #0c141f; + --vscode-notificationLink-foreground: #3794ff; + --vscode-notificationCenterHeader-background: #0c141f; + --vscode-notifications-border: #0c141f; + --vscode-notificationsErrorIcon-foreground: #f48771; + --vscode-notificationsWarningIcon-foreground: #ff0000; + --vscode-notificationsInfoIcon-foreground: #3794ff; + --vscode-window-activeBorder: #6fc3df; + --vscode-window-inactiveBorder: #6fc3df; + --vscode-commandCenter-foreground: #ffffff; + --vscode-commandCenter-activeForeground: #ffffff; + --vscode-commandCenter-border: rgba(255, 255, 255, 0.6); + --vscode-commandCenter-activeBorder: #ffffff; + --vscode-editorCommentsWidget-resolvedBorder: #6fc3df; + --vscode-editorCommentsWidget-unresolvedBorder: #6fc3df; + --vscode-editorCommentsWidget-rangeBackground: rgba(111, 195, 223, 0.1); + --vscode-editorCommentsWidget-rangeBorder: rgba(111, 195, 223, 0.4); + --vscode-editorCommentsWidget-rangeActiveBackground: rgba(111, 195, 223, 0.1); + --vscode-editorCommentsWidget-rangeActiveBorder: rgba(111, 195, 223, 0.4); + --vscode-editorGutter-commentRangeForeground: #ffffff; + --vscode-debugToolBar-background: #000000; + --vscode-debugIcon-startForeground: #89d185; + --vscode-editor-stackFrameHighlightBackground: rgba(255, 255, 0, 0.2); + --vscode-editor-focusedStackFrameHighlightBackground: rgba( + 122, + 189, + 122, + 0.3 + ); + --vscode-mergeEditor-change\.background: rgba(155, 185, 85, 0.2); + --vscode-mergeEditor-change\.word\.background: rgba(156, 204, 44, 0.2); + --vscode-mergeEditor-changeBase\.background: #4b1818; + --vscode-mergeEditor-changeBase\.word\.background: #6f1313; + --vscode-mergeEditor-conflict\.unhandledUnfocused\.border: rgba( + 255, + 166, + 0, + 0.48 + ); + --vscode-mergeEditor-conflict\.unhandledFocused\.border: #ffa600; + --vscode-mergeEditor-conflict\.handledUnfocused\.border: rgba( + 134, + 134, + 134, + 0.29 + ); + --vscode-mergeEditor-conflict\.handledFocused\.border: rgba( + 193, + 193, + 193, + 0.8 + ); + --vscode-mergeEditor-conflict\.handled\.minimapOverViewRuler: rgba( + 173, + 172, + 168, + 0.93 + ); + --vscode-mergeEditor-conflict\.unhandled\.minimapOverViewRuler: #fcba03; + --vscode-mergeEditor-conflictingLines\.background: rgba(255, 234, 0, 0.28); + --vscode-settings-headerForeground: #ffffff; + --vscode-settings-modifiedItemIndicator: #00497a; + --vscode-settings-headerBorder: #6fc3df; + --vscode-settings-sashBorder: #6fc3df; + --vscode-settings-dropdownBackground: #000000; + --vscode-settings-dropdownForeground: #ffffff; + --vscode-settings-dropdownBorder: #6fc3df; + --vscode-settings-dropdownListBorder: #6fc3df; + --vscode-settings-checkboxBackground: #000000; + --vscode-settings-checkboxForeground: #ffffff; + --vscode-settings-checkboxBorder: #6fc3df; + --vscode-settings-textInputBackground: #000000; + --vscode-settings-textInputForeground: #ffffff; + --vscode-settings-textInputBorder: #6fc3df; + --vscode-settings-numberInputBackground: #000000; + --vscode-settings-numberInputForeground: #ffffff; + --vscode-settings-numberInputBorder: #6fc3df; + --vscode-settings-focusedRowBorder: #f38518; + --vscode-terminal-foreground: #ffffff; + --vscode-terminal-selectionBackground: #ffffff; + --vscode-terminal-inactiveSelectionBackground: rgba(255, 255, 255, 0.7); + --vscode-terminal-selectionForeground: #000000; + --vscode-terminalCommandDecoration-defaultBackground: rgba( + 255, + 255, + 255, + 0.5 + ); + --vscode-terminalCommandDecoration-successBackground: #1b81a8; + --vscode-terminalCommandDecoration-errorBackground: #f14c4c; + --vscode-terminalOverviewRuler-cursorForeground: rgba(160, 160, 160, 0.8); + --vscode-terminal-border: #6fc3df; + --vscode-terminal-findMatchBorder: #f38518; + --vscode-terminal-findMatchHighlightBorder: #f38518; + --vscode-terminalOverviewRuler-findMatchForeground: #f38518; + --vscode-testing-iconFailed: #f14c4c; + --vscode-testing-iconErrored: #f14c4c; + --vscode-testing-iconPassed: #73c991; + --vscode-testing-runAction: #73c991; + --vscode-testing-iconQueued: #cca700; + --vscode-testing-iconUnset: #848484; + --vscode-testing-iconSkipped: #848484; + --vscode-testing-peekBorder: #6fc3df; + --vscode-testing-message\.error\.decorationForeground: #ffffff; + --vscode-testing-message\.info\.decorationForeground: rgba( + 255, + 255, + 255, + 0.5 + ); + --vscode-welcomePage-tileBackground: #000000; + --vscode-welcomePage-tileBorder: #6fc3df; + --vscode-welcomePage-progress\.background: #000000; + --vscode-welcomePage-progress\.foreground: #3794ff; + --vscode-debugExceptionWidget-border: #a31515; + --vscode-debugExceptionWidget-background: #420b0d; + --vscode-ports-iconRunningProcessForeground: #ffffff; + --vscode-statusBar-debuggingBackground: #ba592c; + --vscode-statusBar-debuggingForeground: #ffffff; + --vscode-statusBar-debuggingBorder: #6fc3df; + --vscode-editor-inlineValuesForeground: rgba(255, 255, 255, 0.5); + --vscode-editor-inlineValuesBackground: rgba(255, 200, 0, 0.2); + --vscode-editorGutter-modifiedBackground: #1b81a8; + --vscode-editorGutter-addedBackground: #487e02; + --vscode-editorGutter-deletedBackground: #f48771; + --vscode-minimapGutter-modifiedBackground: #1b81a8; + --vscode-minimapGutter-addedBackground: #487e02; + --vscode-minimapGutter-deletedBackground: #f48771; + --vscode-editorOverviewRuler-modifiedForeground: rgba(27, 129, 168, 0.6); + --vscode-editorOverviewRuler-addedForeground: rgba(72, 126, 2, 0.6); + --vscode-editorOverviewRuler-deletedForeground: rgba(244, 135, 113, 0.6); + --vscode-debugIcon-breakpointForeground: #e51400; + --vscode-debugIcon-breakpointDisabledForeground: #848484; + --vscode-debugIcon-breakpointUnverifiedForeground: #848484; + --vscode-debugIcon-breakpointCurrentStackframeForeground: #ffcc00; + --vscode-debugIcon-breakpointStackframeForeground: #89d185; + --vscode-notebook-cellBorderColor: #6fc3df; + --vscode-notebook-focusedEditorBorder: #f38518; + --vscode-notebookStatusSuccessIcon-foreground: #89d185; + --vscode-notebookStatusErrorIcon-foreground: #f48771; + --vscode-notebookStatusRunningIcon-foreground: #ffffff; + --vscode-notebook-cellToolbarSeparator: #6fc3df; + --vscode-notebook-selectedCellBorder: #6fc3df; + --vscode-notebook-inactiveSelectedCellBorder: #f38518; + --vscode-notebook-focusedCellBorder: #f38518; + --vscode-notebook-inactiveFocusedCellBorder: #6fc3df; + --vscode-notebook-cellStatusBarItemHoverBackground: rgba(255, 255, 255, 0.15); + --vscode-notebook-cellInsertionIndicator: #f38518; + --vscode-notebookScrollbarSlider-background: rgba(111, 195, 223, 0.6); + --vscode-notebookScrollbarSlider-hoverBackground: rgba(111, 195, 223, 0.8); + --vscode-notebookScrollbarSlider-activeBackground: #6fc3df; + --vscode-scm-providerBorder: #6fc3df; + --vscode-searchEditor-textInputBorder: #6fc3df; + --vscode-debugTokenExpression-name: #ffffff; + --vscode-debugTokenExpression-value: #ffffff; + --vscode-debugTokenExpression-string: #f48771; + --vscode-debugTokenExpression-boolean: #75bdfe; + --vscode-debugTokenExpression-number: #89d185; + --vscode-debugTokenExpression-error: #f48771; + --vscode-debugView-exceptionLabelForeground: #ffffff; + --vscode-debugView-exceptionLabelBackground: #6c2022; + --vscode-debugView-stateLabelForeground: #ffffff; + --vscode-debugView-stateLabelBackground: rgba(136, 136, 136, 0.27); + --vscode-debugView-valueChangedHighlight: #569cd6; + --vscode-debugConsole-infoForeground: #ffffff; + --vscode-debugConsole-warningForeground: #008000; + --vscode-debugConsole-errorForeground: #f48771; + --vscode-debugConsole-sourceForeground: #ffffff; + --vscode-debugConsoleInputIcon-foreground: #ffffff; + --vscode-debugIcon-pauseForeground: #75beff; + --vscode-debugIcon-stopForeground: #f48771; + --vscode-debugIcon-disconnectForeground: #f48771; + --vscode-debugIcon-restartForeground: #89d185; + --vscode-debugIcon-stepOverForeground: #75beff; + --vscode-debugIcon-stepIntoForeground: #75beff; + --vscode-debugIcon-stepOutForeground: #75beff; + --vscode-debugIcon-continueForeground: #75beff; + --vscode-debugIcon-stepBackForeground: #75beff; + --vscode-extensionButton-separator: rgba(255, 255, 255, 0.4); + --vscode-extensionIcon-starForeground: #ff8e00; + --vscode-extensionIcon-verifiedForeground: #3794ff; + --vscode-extensionIcon-preReleaseForeground: #1d9271; + --vscode-terminal-ansiBlack: #000000; + --vscode-terminal-ansiRed: #cd0000; + --vscode-terminal-ansiGreen: #00cd00; + --vscode-terminal-ansiYellow: #cdcd00; + --vscode-terminal-ansiBlue: #0000ee; + --vscode-terminal-ansiMagenta: #cd00cd; + --vscode-terminal-ansiCyan: #00cdcd; + --vscode-terminal-ansiWhite: #e5e5e5; + --vscode-terminal-ansiBrightBlack: #7f7f7f; + --vscode-terminal-ansiBrightRed: #ff0000; + --vscode-terminal-ansiBrightGreen: #00ff00; + --vscode-terminal-ansiBrightYellow: #ffff00; + --vscode-terminal-ansiBrightBlue: #5c5cff; + --vscode-terminal-ansiBrightMagenta: #ff00ff; + --vscode-terminal-ansiBrightCyan: #00ffff; + --vscode-terminal-ansiBrightWhite: #ffffff; + --vscode-interactive-activeCodeBorder: #6fc3df; + --vscode-interactive-inactiveCodeBorder: #6fc3df; + --vscode-gitDecoration-addedResourceForeground: #a1e3ad; + --vscode-gitDecoration-modifiedResourceForeground: #e2c08d; + --vscode-gitDecoration-deletedResourceForeground: #c74e39; + --vscode-gitDecoration-renamedResourceForeground: #73c991; + --vscode-gitDecoration-untrackedResourceForeground: #73c991; + --vscode-gitDecoration-ignoredResourceForeground: #a7a8a9; + --vscode-gitDecoration-stageModifiedResourceForeground: #e2c08d; + --vscode-gitDecoration-stageDeletedResourceForeground: #c74e39; + --vscode-gitDecoration-conflictingResourceForeground: #c74e39; + --vscode-gitDecoration-submoduleResourceForeground: #8db9e2; + --vscode-testExplorer-errorDecorationBackground: #000000; +} + +/** + * This is copied in the same way, but from the element + */ +body { + background-color: transparent; + color: var(--vscode-editor-foreground); + font-family: var(--vscode-font-family); + font-weight: var(--vscode-font-weight); + font-size: var(--vscode-font-size); + margin: 0; + padding: 0 20px; +} + +/** + * This is used for setting the background on the Storybook preview. + */ +body { + background-color: var(--vscode-editor-background); +} diff --git a/extensions/ql-vscode/src/stories/vscode-theme-github-dark-default.css b/extensions/ql-vscode/src/stories/vscode-theme-github-dark-default.css new file mode 100644 index 000000000..5be183020 --- /dev/null +++ b/extensions/ql-vscode/src/stories/vscode-theme-github-dark-default.css @@ -0,0 +1,674 @@ +/* + * These were copied from VSCode GitHub Dark Default theme from the + * https://marketplace.visualstudio.com/items?itemName=GitHub.github-vscode-theme extension. + * + * To update these, open a webview in VSCode, open the webview developer tools and find the + * iframe hosting the webview. The element will have a style attribute that contains + * the CSS variables. Copy these to this file. + */ +:root { + --vscode-font-family: -apple-system, BlinkMacSystemFont, sans-serif; + --vscode-font-weight: normal; + --vscode-font-size: 13px; + --vscode-editor-font-family: Menlo, Monaco, "Courier New", monospace; + --vscode-editor-font-weight: normal; + --vscode-editor-font-size: 12px; + --vscode-foreground: #c9d1d9; + --vscode-disabledForeground: rgba(204, 204, 204, 0.5); + --vscode-errorForeground: #f85149; + --vscode-descriptionForeground: #8b949e; + --vscode-icon-foreground: #8b949e; + --vscode-focusBorder: #1f6feb; + --vscode-textSeparator-foreground: #21262d; + --vscode-textLink-foreground: #58a6ff; + --vscode-textLink-activeForeground: #58a6ff; + --vscode-textPreformat-foreground: #8b949e; + --vscode-textBlockQuote-background: #010409; + --vscode-textBlockQuote-border: #30363d; + --vscode-textCodeBlock-background: rgba(110, 118, 129, 0.4); + --vscode-widget-shadow: rgba(0, 0, 0, 0.36); + --vscode-input-background: #0d1117; + --vscode-input-foreground: #c9d1d9; + --vscode-input-border: #30363d; + --vscode-inputOption-activeBorder: rgba(0, 122, 204, 0); + --vscode-inputOption-hoverBackground: rgba(90, 93, 94, 0.5); + --vscode-inputOption-activeBackground: rgba(31, 111, 235, 0.4); + --vscode-inputOption-activeForeground: #ffffff; + --vscode-input-placeholderForeground: #484f58; + --vscode-inputValidation-infoBackground: #063b49; + --vscode-inputValidation-infoBorder: #007acc; + --vscode-inputValidation-warningBackground: #352a05; + --vscode-inputValidation-warningBorder: #b89500; + --vscode-inputValidation-errorBackground: #5a1d1d; + --vscode-inputValidation-errorBorder: #be1100; + --vscode-dropdown-background: #161b22; + --vscode-dropdown-listBackground: #161b22; + --vscode-dropdown-foreground: #c9d1d9; + --vscode-dropdown-border: #30363d; + --vscode-button-foreground: #ffffff; + --vscode-button-separator: rgba(255, 255, 255, 0.4); + --vscode-button-background: #238636; + --vscode-button-hoverBackground: #2ea043; + --vscode-button-secondaryForeground: #c9d1d9; + --vscode-button-secondaryBackground: #282e33; + --vscode-button-secondaryHoverBackground: #30363d; + --vscode-badge-background: #1f6feb; + --vscode-badge-foreground: #f0f6fc; + --vscode-scrollbar-shadow: rgba(72, 79, 88, 0.2); + --vscode-scrollbarSlider-background: rgba(110, 118, 129, 0.2); + --vscode-scrollbarSlider-hoverBackground: rgba(110, 118, 129, 0.27); + --vscode-scrollbarSlider-activeBackground: rgba(110, 118, 129, 0.53); + --vscode-progressBar-background: #1f6feb; + --vscode-editorError-foreground: #f14c4c; + --vscode-editorWarning-foreground: #cca700; + --vscode-editorInfo-foreground: #3794ff; + --vscode-editorHint-foreground: rgba(238, 238, 238, 0.7); + --vscode-sash-hoverBorder: #1f6feb; + --vscode-editor-background: #0d1117; + --vscode-editor-foreground: #c9d1d9; + --vscode-editorStickyScroll-background: #0d1117; + --vscode-editorStickyScrollHover-background: #2a2d2e; + --vscode-editorWidget-background: #161b22; + --vscode-editorWidget-foreground: #c9d1d9; + --vscode-editorWidget-border: #454545; + --vscode-quickInput-background: #161b22; + --vscode-quickInput-foreground: #c9d1d9; + --vscode-quickInputTitle-background: rgba(255, 255, 255, 0.1); + --vscode-pickerGroup-foreground: #8b949e; + --vscode-pickerGroup-border: #30363d; + --vscode-keybindingLabel-background: rgba(128, 128, 128, 0.17); + --vscode-keybindingLabel-foreground: #c9d1d9; + --vscode-keybindingLabel-border: rgba(51, 51, 51, 0.6); + --vscode-keybindingLabel-bottomBorder: rgba(68, 68, 68, 0.6); + --vscode-editor-selectionBackground: #264f78; + --vscode-editor-inactiveSelectionBackground: rgba(38, 79, 120, 0.5); + --vscode-editor-selectionHighlightBackground: rgba(63, 185, 80, 0.25); + --vscode-editor-findMatchBackground: #9e6a03; + --vscode-editor-findMatchHighlightBackground: rgba(242, 204, 96, 0.5); + --vscode-editor-findRangeHighlightBackground: rgba(58, 61, 65, 0.4); + --vscode-searchEditor-findMatchBackground: rgba(242, 204, 96, 0.33); + --vscode-editor-hoverHighlightBackground: rgba(38, 79, 120, 0.25); + --vscode-editorHoverWidget-background: #161b22; + --vscode-editorHoverWidget-foreground: #c9d1d9; + --vscode-editorHoverWidget-border: #454545; + --vscode-editorHoverWidget-statusBarBackground: #1a2029; + --vscode-editorLink-activeForeground: #4e94ce; + --vscode-editorInlayHint-foreground: #8b949e; + --vscode-editorInlayHint-background: rgba(139, 148, 158, 0.2); + --vscode-editorInlayHint-typeForeground: #8b949e; + --vscode-editorInlayHint-typeBackground: rgba(139, 148, 158, 0.2); + --vscode-editorInlayHint-parameterForeground: #8b949e; + --vscode-editorInlayHint-parameterBackground: rgba(139, 148, 158, 0.2); + --vscode-editorLightBulb-foreground: #ffcc00; + --vscode-editorLightBulbAutoFix-foreground: #75beff; + --vscode-diffEditor-insertedTextBackground: rgba(35, 134, 54, 0.3); + --vscode-diffEditor-removedTextBackground: rgba(218, 54, 51, 0.3); + --vscode-diffEditor-insertedLineBackground: rgba(35, 134, 54, 0.2); + --vscode-diffEditor-removedLineBackground: rgba(218, 54, 51, 0.2); + --vscode-diffEditor-diagonalFill: rgba(204, 204, 204, 0.2); + --vscode-list-focusBackground: rgba(56, 139, 253, 0.15); + --vscode-list-focusForeground: #c9d1d9; + --vscode-list-focusOutline: #1f6feb; + --vscode-list-activeSelectionBackground: rgba(110, 118, 129, 0.4); + --vscode-list-activeSelectionForeground: #c9d1d9; + --vscode-list-inactiveSelectionBackground: rgba(110, 118, 129, 0.4); + --vscode-list-inactiveSelectionForeground: #c9d1d9; + --vscode-list-inactiveFocusBackground: rgba(56, 139, 253, 0.15); + --vscode-list-hoverBackground: rgba(110, 118, 129, 0.1); + --vscode-list-hoverForeground: #c9d1d9; + --vscode-list-dropBackground: #062f4a; + --vscode-list-highlightForeground: #58a6ff; + --vscode-list-focusHighlightForeground: #58a6ff; + --vscode-list-invalidItemForeground: #b89500; + --vscode-list-errorForeground: #f88070; + --vscode-list-warningForeground: #cca700; + --vscode-listFilterWidget-background: #161b22; + --vscode-listFilterWidget-outline: rgba(0, 0, 0, 0); + --vscode-listFilterWidget-noMatchesOutline: #be1100; + --vscode-listFilterWidget-shadow: rgba(0, 0, 0, 0.36); + --vscode-list-filterMatchBackground: rgba(242, 204, 96, 0.5); + --vscode-tree-indentGuidesStroke: #21262d; + --vscode-tree-tableColumnsBorder: rgba(204, 204, 204, 0.13); + --vscode-tree-tableOddRowsBackground: rgba(201, 209, 217, 0.04); + --vscode-list-deemphasizedForeground: #8c8c8c; + --vscode-checkbox-background: #161b22; + --vscode-checkbox-selectBackground: #161b22; + --vscode-checkbox-foreground: #c9d1d9; + --vscode-checkbox-border: #30363d; + --vscode-checkbox-selectBorder: #161b22; + --vscode-quickInputList-focusForeground: #c9d1d9; + --vscode-quickInputList-focusBackground: rgba(110, 118, 129, 0.4); + --vscode-menu-foreground: #c9d1d9; + --vscode-menu-background: #161b22; + --vscode-menu-selectionForeground: #c9d1d9; + --vscode-menu-selectionBackground: rgba(110, 118, 129, 0.4); + --vscode-menu-separatorBackground: #606060; + --vscode-toolbar-hoverBackground: rgba(90, 93, 94, 0.31); + --vscode-toolbar-activeBackground: rgba(99, 102, 103, 0.31); + --vscode-editor-snippetTabstopHighlightBackground: rgba(124, 124, 124, 0.3); + --vscode-editor-snippetFinalTabstopHighlightBorder: #525252; + --vscode-breadcrumb-foreground: #8b949e; + --vscode-breadcrumb-background: #0d1117; + --vscode-breadcrumb-focusForeground: #c9d1d9; + --vscode-breadcrumb-activeSelectionForeground: #8b949e; + --vscode-breadcrumbPicker-background: #161b22; + --vscode-merge-currentHeaderBackground: rgba(64, 200, 174, 0.5); + --vscode-merge-currentContentBackground: rgba(64, 200, 174, 0.2); + --vscode-merge-incomingHeaderBackground: rgba(64, 166, 255, 0.5); + --vscode-merge-incomingContentBackground: rgba(64, 166, 255, 0.2); + --vscode-merge-commonHeaderBackground: rgba(96, 96, 96, 0.4); + --vscode-merge-commonContentBackground: rgba(96, 96, 96, 0.16); + --vscode-editorOverviewRuler-currentContentForeground: rgba( + 64, + 200, + 174, + 0.5 + ); + --vscode-editorOverviewRuler-incomingContentForeground: rgba( + 64, + 166, + 255, + 0.5 + ); + --vscode-editorOverviewRuler-commonContentForeground: rgba(96, 96, 96, 0.4); + --vscode-editorOverviewRuler-findMatchForeground: rgba(209, 134, 22, 0.49); + --vscode-editorOverviewRuler-selectionHighlightForeground: rgba( + 160, + 160, + 160, + 0.8 + ); + --vscode-minimap-findMatchHighlight: #d18616; + --vscode-minimap-selectionOccurrenceHighlight: #676767; + --vscode-minimap-selectionHighlight: #264f78; + --vscode-minimap-errorHighlight: rgba(255, 18, 18, 0.7); + --vscode-minimap-warningHighlight: #cca700; + --vscode-minimap-foregroundOpacity: #000000; + --vscode-minimapSlider-background: rgba(110, 118, 129, 0.1); + --vscode-minimapSlider-hoverBackground: rgba(110, 118, 129, 0.14); + --vscode-minimapSlider-activeBackground: rgba(110, 118, 129, 0.27); + --vscode-problemsErrorIcon-foreground: #f14c4c; + --vscode-problemsWarningIcon-foreground: #cca700; + --vscode-problemsInfoIcon-foreground: #3794ff; + --vscode-charts-foreground: #c9d1d9; + --vscode-charts-lines: rgba(201, 209, 217, 0.5); + --vscode-charts-red: #f14c4c; + --vscode-charts-blue: #3794ff; + --vscode-charts-yellow: #cca700; + --vscode-charts-orange: #d18616; + --vscode-charts-green: #89d185; + --vscode-charts-purple: #b180d7; + --vscode-symbolIcon-arrayForeground: #f0883e; + --vscode-symbolIcon-booleanForeground: #58a6ff; + --vscode-symbolIcon-classForeground: #f0883e; + --vscode-symbolIcon-colorForeground: #79c0ff; + --vscode-symbolIcon-constantForeground: #c9d1d9; + --vscode-symbolIcon-constructorForeground: #d2a8ff; + --vscode-symbolIcon-enumeratorForeground: #f0883e; + --vscode-symbolIcon-enumeratorMemberForeground: #58a6ff; + --vscode-symbolIcon-eventForeground: #6e7681; + --vscode-symbolIcon-fieldForeground: #f0883e; + --vscode-symbolIcon-fileForeground: #d29922; + --vscode-symbolIcon-folderForeground: #d29922; + --vscode-symbolIcon-functionForeground: #bc8cff; + --vscode-symbolIcon-interfaceForeground: #f0883e; + --vscode-symbolIcon-keyForeground: #58a6ff; + --vscode-symbolIcon-keywordForeground: #ff7b72; + --vscode-symbolIcon-methodForeground: #bc8cff; + --vscode-symbolIcon-moduleForeground: #ff7b72; + --vscode-symbolIcon-namespaceForeground: #ff7b72; + --vscode-symbolIcon-nullForeground: #58a6ff; + --vscode-symbolIcon-numberForeground: #3fb950; + --vscode-symbolIcon-objectForeground: #f0883e; + --vscode-symbolIcon-operatorForeground: #79c0ff; + --vscode-symbolIcon-packageForeground: #f0883e; + --vscode-symbolIcon-propertyForeground: #f0883e; + --vscode-symbolIcon-referenceForeground: #58a6ff; + --vscode-symbolIcon-snippetForeground: #58a6ff; + --vscode-symbolIcon-stringForeground: #79c0ff; + --vscode-symbolIcon-structForeground: #f0883e; + --vscode-symbolIcon-textForeground: #79c0ff; + --vscode-symbolIcon-typeParameterForeground: #79c0ff; + --vscode-symbolIcon-unitForeground: #58a6ff; + --vscode-symbolIcon-variableForeground: #f0883e; + --vscode-editor-lineHighlightBackground: rgba(110, 118, 129, 0.1); + --vscode-editor-lineHighlightBorder: #282828; + --vscode-editor-rangeHighlightBackground: rgba(255, 255, 255, 0.04); + --vscode-editor-symbolHighlightBackground: rgba(242, 204, 96, 0.5); + --vscode-editorCursor-foreground: #58a6ff; + --vscode-editorWhitespace-foreground: #484f58; + --vscode-editorIndentGuide-background: rgba(201, 209, 217, 0.12); + --vscode-editorIndentGuide-activeBackground: rgba(201, 209, 217, 0.24); + --vscode-editorLineNumber-foreground: #6e7681; + --vscode-editorActiveLineNumber-foreground: #c6c6c6; + --vscode-editorLineNumber-activeForeground: #c9d1d9; + --vscode-editorRuler-foreground: #5a5a5a; + --vscode-editorCodeLens-foreground: #999999; + --vscode-editorBracketMatch-background: rgba(63, 185, 80, 0.25); + --vscode-editorBracketMatch-border: rgba(63, 185, 80, 0.6); + --vscode-editorOverviewRuler-border: #010409; + --vscode-editorGutter-background: #0d1117; + --vscode-editorUnnecessaryCode-opacity: rgba(0, 0, 0, 0.67); + --vscode-editorGhostText-foreground: rgba(255, 255, 255, 0.34); + --vscode-editorOverviewRuler-rangeHighlightForeground: rgba(0, 122, 204, 0.6); + --vscode-editorOverviewRuler-errorForeground: rgba(255, 18, 18, 0.7); + --vscode-editorOverviewRuler-warningForeground: #cca700; + --vscode-editorOverviewRuler-infoForeground: #3794ff; + --vscode-editorBracketHighlight-foreground1: #79c0ff; + --vscode-editorBracketHighlight-foreground2: #56d364; + --vscode-editorBracketHighlight-foreground3: #e3b341; + --vscode-editorBracketHighlight-foreground4: #ffa198; + --vscode-editorBracketHighlight-foreground5: #ff9bce; + --vscode-editorBracketHighlight-foreground6: #d2a8ff; + --vscode-editorBracketHighlight-unexpectedBracket\.foreground: #8b949e; + --vscode-editorBracketPairGuide-background1: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background2: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background3: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background4: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background5: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background6: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground1: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground2: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground3: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground4: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground5: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground6: rgba(0, 0, 0, 0); + --vscode-editorUnicodeHighlight-border: #bd9b03; + --vscode-editorUnicodeHighlight-background: rgba(189, 155, 3, 0.15); + --vscode-editorHoverWidget-highlightForeground: #58a6ff; + --vscode-editorOverviewRuler-bracketMatchForeground: #a0a0a0; + --vscode-editor-foldBackground: rgba(110, 118, 129, 0.1); + --vscode-editorGutter-foldingControlForeground: #8b949e; + --vscode-editor-linkedEditingBackground: rgba(88, 166, 255, 0.07); + --vscode-editor-wordHighlightBackground: rgba(110, 118, 129, 0.5); + --vscode-editor-wordHighlightStrongBackground: rgba(110, 118, 129, 0.3); + --vscode-editor-wordHighlightBorder: rgba(110, 118, 129, 0.6); + --vscode-editor-wordHighlightStrongBorder: rgba(110, 118, 129, 0.6); + --vscode-editorOverviewRuler-wordHighlightForeground: rgba( + 160, + 160, + 160, + 0.8 + ); + --vscode-editorOverviewRuler-wordHighlightStrongForeground: rgba( + 192, + 160, + 192, + 0.8 + ); + --vscode-peekViewTitle-background: rgba(55, 148, 255, 0.1); + --vscode-peekViewTitleLabel-foreground: #ffffff; + --vscode-peekViewTitleDescription-foreground: rgba(204, 204, 204, 0.7); + --vscode-peekView-border: #3794ff; + --vscode-peekViewResult-background: #0d1117; + --vscode-peekViewResult-lineForeground: #bbbbbb; + --vscode-peekViewResult-fileForeground: #ffffff; + --vscode-peekViewResult-selectionBackground: rgba(51, 153, 255, 0.2); + --vscode-peekViewResult-selectionForeground: #ffffff; + --vscode-peekViewEditor-background: rgba(110, 118, 129, 0.1); + --vscode-peekViewEditorGutter-background: rgba(110, 118, 129, 0.1); + --vscode-peekViewResult-matchHighlightBackground: rgba(187, 128, 9, 0.4); + --vscode-peekViewEditor-matchHighlightBackground: rgba(187, 128, 9, 0.4); + --vscode-editorMarkerNavigationError-background: #f14c4c; + --vscode-editorMarkerNavigationError-headerBackground: rgba(241, 76, 76, 0.1); + --vscode-editorMarkerNavigationWarning-background: #cca700; + --vscode-editorMarkerNavigationWarning-headerBackground: rgba( + 204, + 167, + 0, + 0.1 + ); + --vscode-editorMarkerNavigationInfo-background: #3794ff; + --vscode-editorMarkerNavigationInfo-headerBackground: rgba(55, 148, 255, 0.1); + --vscode-editorMarkerNavigation-background: #0d1117; + --vscode-editorSuggestWidget-background: #161b22; + --vscode-editorSuggestWidget-border: #454545; + --vscode-editorSuggestWidget-foreground: #c9d1d9; + --vscode-editorSuggestWidget-selectedForeground: #c9d1d9; + --vscode-editorSuggestWidget-selectedBackground: rgba(110, 118, 129, 0.4); + --vscode-editorSuggestWidget-highlightForeground: #58a6ff; + --vscode-editorSuggestWidget-focusHighlightForeground: #58a6ff; + --vscode-editorSuggestWidgetStatus-foreground: rgba(201, 209, 217, 0.5); + --vscode-tab-activeBackground: #0d1117; + --vscode-tab-unfocusedActiveBackground: #0d1117; + --vscode-tab-inactiveBackground: #010409; + --vscode-tab-unfocusedInactiveBackground: #010409; + --vscode-tab-activeForeground: #c9d1d9; + --vscode-tab-inactiveForeground: #8b949e; + --vscode-tab-unfocusedActiveForeground: rgba(201, 209, 217, 0.5); + --vscode-tab-unfocusedInactiveForeground: rgba(139, 148, 158, 0.5); + --vscode-tab-hoverBackground: #0d1117; + --vscode-tab-unfocusedHoverBackground: rgba(110, 118, 129, 0.1); + --vscode-tab-border: #30363d; + --vscode-tab-lastPinnedBorder: #21262d; + --vscode-tab-activeBorder: #0d1117; + --vscode-tab-unfocusedActiveBorder: #0d1117; + --vscode-tab-activeBorderTop: #f78166; + --vscode-tab-unfocusedActiveBorderTop: #30363d; + --vscode-tab-activeModifiedBorder: #3399cc; + --vscode-tab-inactiveModifiedBorder: rgba(51, 153, 204, 0.5); + --vscode-tab-unfocusedActiveModifiedBorder: rgba(51, 153, 204, 0.5); + --vscode-tab-unfocusedInactiveModifiedBorder: rgba(51, 153, 204, 0.25); + --vscode-editorPane-background: #0d1117; + --vscode-editorGroupHeader-tabsBackground: #010409; + --vscode-editorGroupHeader-tabsBorder: #30363d; + --vscode-editorGroupHeader-noTabsBackground: #0d1117; + --vscode-editorGroup-border: #30363d; + --vscode-editorGroup-dropBackground: rgba(83, 89, 93, 0.5); + --vscode-editorGroup-dropIntoPromptForeground: #c9d1d9; + --vscode-editorGroup-dropIntoPromptBackground: #161b22; + --vscode-sideBySideEditor-horizontalBorder: #30363d; + --vscode-sideBySideEditor-verticalBorder: #30363d; + --vscode-panel-background: #010409; + --vscode-panel-border: #30363d; + --vscode-panelTitle-activeForeground: #c9d1d9; + --vscode-panelTitle-inactiveForeground: #8b949e; + --vscode-panelTitle-activeBorder: #f78166; + --vscode-panelInput-border: #30363d; + --vscode-panel-dropBorder: #c9d1d9; + --vscode-panelSection-dropBackground: rgba(83, 89, 93, 0.5); + --vscode-panelSectionHeader-background: rgba(128, 128, 128, 0.2); + --vscode-panelSection-border: #30363d; + --vscode-banner-background: rgba(110, 118, 129, 0.4); + --vscode-banner-foreground: #c9d1d9; + --vscode-banner-iconForeground: #3794ff; + --vscode-statusBar-foreground: #8b949e; + --vscode-statusBar-noFolderForeground: #8b949e; + --vscode-statusBar-background: #0d1117; + --vscode-statusBar-noFolderBackground: #0d1117; + --vscode-statusBar-border: #30363d; + --vscode-statusBar-focusBorder: rgba(31, 111, 235, 0.5); + --vscode-statusBar-noFolderBorder: #30363d; + --vscode-statusBarItem-activeBackground: rgba(201, 209, 217, 0.12); + --vscode-statusBarItem-focusBorder: #1f6feb; + --vscode-statusBarItem-hoverBackground: rgba(201, 209, 217, 0.08); + --vscode-statusBarItem-compactHoverBackground: rgba(255, 255, 255, 0.2); + --vscode-statusBarItem-prominentForeground: #8b949e; + --vscode-statusBarItem-prominentBackground: rgba(110, 118, 129, 0.4); + --vscode-statusBarItem-prominentHoverBackground: rgba(0, 0, 0, 0.3); + --vscode-statusBarItem-errorBackground: #b91007; + --vscode-statusBarItem-errorForeground: #ffffff; + --vscode-statusBarItem-warningBackground: #7a6400; + --vscode-statusBarItem-warningForeground: #ffffff; + --vscode-activityBar-background: #0d1117; + --vscode-activityBar-foreground: #c9d1d9; + --vscode-activityBar-inactiveForeground: #8b949e; + --vscode-activityBar-border: #30363d; + --vscode-activityBar-activeBorder: #f78166; + --vscode-activityBar-dropBorder: #c9d1d9; + --vscode-activityBarBadge-background: #1f6feb; + --vscode-activityBarBadge-foreground: #f0f6fc; + --vscode-activityBarItem-profilesForeground: #8b949e; + --vscode-activityBarItem-profilesHoverForeground: #c9d1d9; + --vscode-activityBarItem-profilesBackground: #141a23; + --vscode-statusBarItem-remoteBackground: #30363d; + --vscode-statusBarItem-remoteForeground: #c9d1d9; + --vscode-extensionBadge-remoteBackground: #1f6feb; + --vscode-extensionBadge-remoteForeground: #f0f6fc; + --vscode-sideBar-background: #010409; + --vscode-sideBar-foreground: #c9d1d9; + --vscode-sideBar-border: #30363d; + --vscode-sideBarTitle-foreground: #c9d1d9; + --vscode-sideBar-dropBackground: rgba(83, 89, 93, 0.5); + --vscode-sideBarSectionHeader-background: #010409; + --vscode-sideBarSectionHeader-foreground: #c9d1d9; + --vscode-sideBarSectionHeader-border: #30363d; + --vscode-titleBar-activeForeground: #8b949e; + --vscode-titleBar-inactiveForeground: #8b949e; + --vscode-titleBar-activeBackground: #0d1117; + --vscode-titleBar-inactiveBackground: #010409; + --vscode-titleBar-border: #30363d; + --vscode-menubar-selectionForeground: #8b949e; + --vscode-menubar-selectionBackground: rgba(90, 93, 94, 0.31); + --vscode-notifications-foreground: #c9d1d9; + --vscode-notifications-background: #161b22; + --vscode-notificationLink-foreground: #58a6ff; + --vscode-notificationCenterHeader-foreground: #8b949e; + --vscode-notificationCenterHeader-background: #161b22; + --vscode-notifications-border: #30363d; + --vscode-notificationsErrorIcon-foreground: #f85149; + --vscode-notificationsWarningIcon-foreground: #d29922; + --vscode-notificationsInfoIcon-foreground: #58a6ff; + --vscode-commandCenter-foreground: #8b949e; + --vscode-commandCenter-activeForeground: #8b949e; + --vscode-commandCenter-inactiveForeground: #8b949e; + --vscode-commandCenter-background: rgba(255, 255, 255, 0.05); + --vscode-commandCenter-activeBackground: rgba(255, 255, 255, 0.08); + --vscode-commandCenter-border: rgba(139, 148, 158, 0.2); + --vscode-commandCenter-activeBorder: rgba(139, 148, 158, 0.3); + --vscode-commandCenter-inactiveBorder: rgba(139, 148, 158, 0.25); + --vscode-editorCommentsWidget-resolvedBorder: rgba(204, 204, 204, 0.5); + --vscode-editorCommentsWidget-unresolvedBorder: #3794ff; + --vscode-editorCommentsWidget-rangeBackground: rgba(55, 148, 255, 0.1); + --vscode-editorCommentsWidget-rangeBorder: rgba(55, 148, 255, 0.4); + --vscode-editorCommentsWidget-rangeActiveBackground: rgba(55, 148, 255, 0.1); + --vscode-editorCommentsWidget-rangeActiveBorder: rgba(55, 148, 255, 0.4); + --vscode-editorGutter-commentRangeForeground: rgba(110, 118, 129, 0.4); + --vscode-debugToolBar-background: #161b22; + --vscode-debugIcon-startForeground: #89d185; + --vscode-editor-stackFrameHighlightBackground: rgba(187, 128, 9, 0.4); + --vscode-editor-focusedStackFrameHighlightBackground: rgba(46, 160, 67, 0.4); + --vscode-mergeEditor-change\.background: rgba(155, 185, 85, 0.2); + --vscode-mergeEditor-change\.word\.background: rgba(156, 204, 44, 0.2); + --vscode-mergeEditor-changeBase\.background: #4b1818; + --vscode-mergeEditor-changeBase\.word\.background: #6f1313; + --vscode-mergeEditor-conflict\.unhandledUnfocused\.border: rgba( + 255, + 166, + 0, + 0.48 + ); + --vscode-mergeEditor-conflict\.unhandledFocused\.border: #ffa600; + --vscode-mergeEditor-conflict\.handledUnfocused\.border: rgba( + 134, + 134, + 134, + 0.29 + ); + --vscode-mergeEditor-conflict\.handledFocused\.border: rgba( + 193, + 193, + 193, + 0.8 + ); + --vscode-mergeEditor-conflict\.handled\.minimapOverViewRuler: rgba( + 173, + 172, + 168, + 0.93 + ); + --vscode-mergeEditor-conflict\.unhandled\.minimapOverViewRuler: #fcba03; + --vscode-mergeEditor-conflictingLines\.background: rgba(255, 234, 0, 0.28); + --vscode-mergeEditor-conflict\.input1\.background: rgba(64, 200, 174, 0.2); + --vscode-mergeEditor-conflict\.input2\.background: rgba(64, 166, 255, 0.2); + --vscode-settings-headerForeground: #8b949e; + --vscode-settings-modifiedItemIndicator: rgba(187, 128, 9, 0.4); + --vscode-settings-headerBorder: #30363d; + --vscode-settings-sashBorder: #30363d; + --vscode-settings-dropdownBackground: #161b22; + --vscode-settings-dropdownForeground: #c9d1d9; + --vscode-settings-dropdownBorder: #30363d; + --vscode-settings-dropdownListBorder: #454545; + --vscode-settings-checkboxBackground: #161b22; + --vscode-settings-checkboxForeground: #c9d1d9; + --vscode-settings-checkboxBorder: #30363d; + --vscode-settings-textInputBackground: #0d1117; + --vscode-settings-textInputForeground: #c9d1d9; + --vscode-settings-textInputBorder: #30363d; + --vscode-settings-numberInputBackground: #0d1117; + --vscode-settings-numberInputForeground: #c9d1d9; + --vscode-settings-numberInputBorder: #30363d; + --vscode-settings-focusedRowBackground: rgba(110, 118, 129, 0.06); + --vscode-settings-rowHoverBackground: rgba(110, 118, 129, 0.03); + --vscode-settings-focusedRowBorder: #1f6feb; + --vscode-terminal-foreground: #c9d1d9; + --vscode-terminal-selectionBackground: #264f78; + --vscode-terminal-inactiveSelectionBackground: rgba(38, 79, 120, 0.5); + --vscode-terminalCommandDecoration-defaultBackground: rgba( + 255, + 255, + 255, + 0.25 + ); + --vscode-terminalCommandDecoration-successBackground: #1b81a8; + --vscode-terminalCommandDecoration-errorBackground: #f14c4c; + --vscode-terminalOverviewRuler-cursorForeground: rgba(160, 160, 160, 0.8); + --vscode-terminal-border: #30363d; + --vscode-terminal-findMatchBackground: #9e6a03; + --vscode-terminal-findMatchHighlightBackground: rgba(242, 204, 96, 0.5); + --vscode-terminalOverviewRuler-findMatchForeground: rgba(209, 134, 22, 0.49); + --vscode-terminal-dropBackground: rgba(83, 89, 93, 0.5); + --vscode-terminal-tab\.activeBorder: #0d1117; + --vscode-testing-iconFailed: #f14c4c; + --vscode-testing-iconErrored: #f14c4c; + --vscode-testing-iconPassed: #73c991; + --vscode-testing-runAction: #73c991; + --vscode-testing-iconQueued: #cca700; + --vscode-testing-iconUnset: #848484; + --vscode-testing-iconSkipped: #848484; + --vscode-testing-peekBorder: #f14c4c; + --vscode-testing-peekHeaderBackground: rgba(241, 76, 76, 0.1); + --vscode-testing-message\.error\.decorationForeground: #f14c4c; + --vscode-testing-message\.error\.lineBackground: rgba(255, 0, 0, 0.2); + --vscode-testing-message\.info\.decorationForeground: rgba( + 201, + 209, + 217, + 0.5 + ); + --vscode-welcomePage-tileBackground: #161b22; + --vscode-welcomePage-tileHoverBackground: #1a2029; + --vscode-welcomePage-tileBorder: rgba(255, 255, 255, 0.1); + --vscode-welcomePage-progress\.background: #0d1117; + --vscode-welcomePage-progress\.foreground: #58a6ff; + --vscode-walkthrough-stepTitle\.foreground: #ffffff; + --vscode-debugExceptionWidget-border: #a31515; + --vscode-debugExceptionWidget-background: #420b0d; + --vscode-ports-iconRunningProcessForeground: #30363d; + --vscode-statusBar-debuggingBackground: #da3633; + --vscode-statusBar-debuggingForeground: #f0f6fc; + --vscode-statusBar-debuggingBorder: #30363d; + --vscode-editor-inlineValuesForeground: rgba(255, 255, 255, 0.5); + --vscode-editor-inlineValuesBackground: rgba(255, 200, 0, 0.2); + --vscode-editorGutter-modifiedBackground: rgba(187, 128, 9, 0.4); + --vscode-editorGutter-addedBackground: rgba(46, 160, 67, 0.4); + --vscode-editorGutter-deletedBackground: rgba(248, 81, 73, 0.4); + --vscode-minimapGutter-modifiedBackground: rgba(187, 128, 9, 0.4); + --vscode-minimapGutter-addedBackground: rgba(46, 160, 67, 0.4); + --vscode-minimapGutter-deletedBackground: rgba(248, 81, 73, 0.4); + --vscode-editorOverviewRuler-modifiedForeground: rgba(187, 128, 9, 0.24); + --vscode-editorOverviewRuler-addedForeground: rgba(46, 160, 67, 0.24); + --vscode-editorOverviewRuler-deletedForeground: rgba(248, 81, 73, 0.24); + --vscode-debugIcon-breakpointForeground: #f85149; + --vscode-debugIcon-breakpointDisabledForeground: #848484; + --vscode-debugIcon-breakpointUnverifiedForeground: #848484; + --vscode-debugIcon-breakpointCurrentStackframeForeground: #ffcc00; + --vscode-debugIcon-breakpointStackframeForeground: #89d185; + --vscode-notebook-cellBorderColor: rgba(110, 118, 129, 0.4); + --vscode-notebook-focusedEditorBorder: #1f6feb; + --vscode-notebookStatusSuccessIcon-foreground: #89d185; + --vscode-notebookStatusErrorIcon-foreground: #f85149; + --vscode-notebookStatusRunningIcon-foreground: #c9d1d9; + --vscode-notebook-cellToolbarSeparator: rgba(128, 128, 128, 0.35); + --vscode-notebook-selectedCellBackground: rgba(110, 118, 129, 0.4); + --vscode-notebook-selectedCellBorder: rgba(110, 118, 129, 0.4); + --vscode-notebook-focusedCellBorder: #1f6feb; + --vscode-notebook-inactiveFocusedCellBorder: rgba(110, 118, 129, 0.4); + --vscode-notebook-cellStatusBarItemHoverBackground: rgba(255, 255, 255, 0.15); + --vscode-notebook-cellInsertionIndicator: #1f6feb; + --vscode-notebookScrollbarSlider-background: rgba(110, 118, 129, 0.2); + --vscode-notebookScrollbarSlider-hoverBackground: rgba(110, 118, 129, 0.27); + --vscode-notebookScrollbarSlider-activeBackground: rgba(110, 118, 129, 0.53); + --vscode-notebook-symbolHighlightBackground: rgba(255, 255, 255, 0.04); + --vscode-notebook-cellEditorBackground: #010409; + --vscode-notebook-editorBackground: #0d1117; + --vscode-keybindingTable-headerBackground: rgba(201, 209, 217, 0.04); + --vscode-keybindingTable-rowsBackground: rgba(201, 209, 217, 0.04); + --vscode-scm-providerBorder: #454545; + --vscode-searchEditor-textInputBorder: #30363d; + --vscode-debugTokenExpression-name: #79c0ff; + --vscode-debugTokenExpression-value: #a5d6ff; + --vscode-debugTokenExpression-string: #a5d6ff; + --vscode-debugTokenExpression-boolean: #56d364; + --vscode-debugTokenExpression-number: #56d364; + --vscode-debugTokenExpression-error: #ffa198; + --vscode-debugView-exceptionLabelForeground: #c9d1d9; + --vscode-debugView-exceptionLabelBackground: #6c2022; + --vscode-debugView-stateLabelForeground: #c9d1d9; + --vscode-debugView-stateLabelBackground: rgba(136, 136, 136, 0.27); + --vscode-debugView-valueChangedHighlight: #569cd6; + --vscode-debugConsole-infoForeground: #8b949e; + --vscode-debugConsole-warningForeground: #d29922; + --vscode-debugConsole-errorForeground: #ffa198; + --vscode-debugConsole-sourceForeground: #e3b341; + --vscode-debugConsoleInputIcon-foreground: #bc8cff; + --vscode-debugIcon-pauseForeground: #75beff; + --vscode-debugIcon-stopForeground: #f48771; + --vscode-debugIcon-disconnectForeground: #f48771; + --vscode-debugIcon-restartForeground: #89d185; + --vscode-debugIcon-stepOverForeground: #75beff; + --vscode-debugIcon-stepIntoForeground: #75beff; + --vscode-debugIcon-stepOutForeground: #75beff; + --vscode-debugIcon-continueForeground: #75beff; + --vscode-debugIcon-stepBackForeground: #75beff; + --vscode-extensionButton-background: #238636; + --vscode-extensionButton-foreground: #ffffff; + --vscode-extensionButton-hoverBackground: #2ea043; + --vscode-extensionButton-separator: rgba(255, 255, 255, 0.4); + --vscode-extensionButton-prominentBackground: #238636; + --vscode-extensionButton-prominentForeground: #ffffff; + --vscode-extensionButton-prominentHoverBackground: #2ea043; + --vscode-extensionIcon-starForeground: #ff8e00; + --vscode-extensionIcon-verifiedForeground: #58a6ff; + --vscode-extensionIcon-preReleaseForeground: #1d9271; + --vscode-extensionIcon-sponsorForeground: #d758b3; + --vscode-terminal-ansiBlack: #484f58; + --vscode-terminal-ansiRed: #ff7b72; + --vscode-terminal-ansiGreen: #3fb950; + --vscode-terminal-ansiYellow: #d29922; + --vscode-terminal-ansiBlue: #58a6ff; + --vscode-terminal-ansiMagenta: #bc8cff; + --vscode-terminal-ansiCyan: #39c5cf; + --vscode-terminal-ansiWhite: #b1bac4; + --vscode-terminal-ansiBrightBlack: #6e7681; + --vscode-terminal-ansiBrightRed: #ffa198; + --vscode-terminal-ansiBrightGreen: #56d364; + --vscode-terminal-ansiBrightYellow: #e3b341; + --vscode-terminal-ansiBrightBlue: #79c0ff; + --vscode-terminal-ansiBrightMagenta: #d2a8ff; + --vscode-terminal-ansiBrightCyan: #56d4dd; + --vscode-terminal-ansiBrightWhite: #f0f6fc; + --vscode-interactive-activeCodeBorder: #3794ff; + --vscode-interactive-inactiveCodeBorder: rgba(110, 118, 129, 0.4); + --vscode-gitDecoration-addedResourceForeground: #3fb950; + --vscode-gitDecoration-modifiedResourceForeground: #d29922; + --vscode-gitDecoration-deletedResourceForeground: #f85149; + --vscode-gitDecoration-renamedResourceForeground: #73c991; + --vscode-gitDecoration-untrackedResourceForeground: #3fb950; + --vscode-gitDecoration-ignoredResourceForeground: #484f58; + --vscode-gitDecoration-stageModifiedResourceForeground: #e2c08d; + --vscode-gitDecoration-stageDeletedResourceForeground: #c74e39; + --vscode-gitDecoration-conflictingResourceForeground: #db6d28; + --vscode-gitDecoration-submoduleResourceForeground: #8b949e; + --vscode-testExplorer-errorDecorationBackground: #5a1d1d; +} + +/** + * This is copied in the same way, but from the element + */ +body { + background-color: transparent; + color: var(--vscode-editor-foreground); + font-family: var(--vscode-font-family); + font-weight: var(--vscode-font-weight); + font-size: var(--vscode-font-size); + margin: 0; + padding: 0 20px; +} + +/** + * This is used for setting the background on the Storybook preview. + */ +body { + background-color: var(--vscode-editor-background); +} diff --git a/extensions/ql-vscode/src/stories/vscode-theme-github-light-default.css b/extensions/ql-vscode/src/stories/vscode-theme-github-light-default.css new file mode 100644 index 000000000..641e38f84 --- /dev/null +++ b/extensions/ql-vscode/src/stories/vscode-theme-github-light-default.css @@ -0,0 +1,659 @@ +/* + * These were copied from VSCode GitHub Light Default theme from the + * https://marketplace.visualstudio.com/items?itemName=GitHub.github-vscode-theme extension. + * + * To update these, open a webview in VSCode, open the webview developer tools and find the + * iframe hosting the webview. The element will have a style attribute that contains + * the CSS variables. Copy these to this file. + */ +:root { + --vscode-font-family: -apple-system, BlinkMacSystemFont, sans-serif; + --vscode-font-weight: normal; + --vscode-font-size: 13px; + --vscode-editor-font-family: Menlo, Monaco, "Courier New", monospace; + --vscode-editor-font-weight: normal; + --vscode-editor-font-size: 12px; + --vscode-foreground: #24292f; + --vscode-disabledForeground: rgba(97, 97, 97, 0.5); + --vscode-errorForeground: #cf222e; + --vscode-descriptionForeground: #57606a; + --vscode-icon-foreground: #57606a; + --vscode-focusBorder: #0969da; + --vscode-textSeparator-foreground: #d8dee4; + --vscode-textLink-foreground: #0969da; + --vscode-textLink-activeForeground: #0969da; + --vscode-textPreformat-foreground: #57606a; + --vscode-textBlockQuote-background: #f6f8fa; + --vscode-textBlockQuote-border: #d0d7de; + --vscode-textCodeBlock-background: rgba(175, 184, 193, 0.2); + --vscode-widget-shadow: rgba(0, 0, 0, 0.16); + --vscode-input-background: #ffffff; + --vscode-input-foreground: #24292f; + --vscode-input-border: #d0d7de; + --vscode-inputOption-activeBorder: rgba(0, 122, 204, 0); + --vscode-inputOption-hoverBackground: rgba(184, 184, 184, 0.31); + --vscode-inputOption-activeBackground: rgba(9, 105, 218, 0.2); + --vscode-inputOption-activeForeground: #000000; + --vscode-input-placeholderForeground: #6e7781; + --vscode-inputValidation-infoBackground: #d6ecf2; + --vscode-inputValidation-infoBorder: #007acc; + --vscode-inputValidation-warningBackground: #f6f5d2; + --vscode-inputValidation-warningBorder: #b89500; + --vscode-inputValidation-errorBackground: #f2dede; + --vscode-inputValidation-errorBorder: #be1100; + --vscode-dropdown-background: #ffffff; + --vscode-dropdown-listBackground: #ffffff; + --vscode-dropdown-foreground: #24292f; + --vscode-dropdown-border: #d0d7de; + --vscode-button-foreground: #ffffff; + --vscode-button-separator: rgba(255, 255, 255, 0.4); + --vscode-button-background: #2da44e; + --vscode-button-hoverBackground: #2c974b; + --vscode-button-secondaryForeground: #24292f; + --vscode-button-secondaryBackground: #ebecf0; + --vscode-button-secondaryHoverBackground: #f3f4f6; + --vscode-badge-background: #0969da; + --vscode-badge-foreground: #ffffff; + --vscode-scrollbar-shadow: rgba(110, 119, 129, 0.2); + --vscode-scrollbarSlider-background: rgba(140, 149, 159, 0.2); + --vscode-scrollbarSlider-hoverBackground: rgba(140, 149, 159, 0.27); + --vscode-scrollbarSlider-activeBackground: rgba(140, 149, 159, 0.53); + --vscode-progressBar-background: #0969da; + --vscode-editorError-foreground: #e51400; + --vscode-editorWarning-foreground: #bf8803; + --vscode-editorInfo-foreground: #1a85ff; + --vscode-editorHint-foreground: #6c6c6c; + --vscode-sash-hoverBorder: #0969da; + --vscode-editor-background: #ffffff; + --vscode-editor-foreground: #24292f; + --vscode-editorStickyScroll-background: #ffffff; + --vscode-editorStickyScrollHover-background: #f0f0f0; + --vscode-editorWidget-background: #ffffff; + --vscode-editorWidget-foreground: #24292f; + --vscode-editorWidget-border: #c8c8c8; + --vscode-quickInput-background: #ffffff; + --vscode-quickInput-foreground: #24292f; + --vscode-quickInputTitle-background: rgba(0, 0, 0, 0.06); + --vscode-pickerGroup-foreground: #57606a; + --vscode-pickerGroup-border: #d0d7de; + --vscode-keybindingLabel-background: rgba(221, 221, 221, 0.4); + --vscode-keybindingLabel-foreground: #24292f; + --vscode-keybindingLabel-border: rgba(204, 204, 204, 0.4); + --vscode-keybindingLabel-bottomBorder: rgba(187, 187, 187, 0.4); + --vscode-editor-selectionBackground: #add6ff; + --vscode-editor-inactiveSelectionBackground: rgba(173, 214, 255, 0.5); + --vscode-editor-selectionHighlightBackground: rgba(74, 194, 107, 0.25); + --vscode-editor-findMatchBackground: #bf8700; + --vscode-editor-findMatchHighlightBackground: rgba(250, 225, 125, 0.5); + --vscode-editor-findRangeHighlightBackground: rgba(180, 180, 180, 0.3); + --vscode-searchEditor-findMatchBackground: rgba(250, 225, 125, 0.33); + --vscode-editor-hoverHighlightBackground: rgba(173, 214, 255, 0.15); + --vscode-editorHoverWidget-background: #ffffff; + --vscode-editorHoverWidget-foreground: #24292f; + --vscode-editorHoverWidget-border: #c8c8c8; + --vscode-editorHoverWidget-statusBarBackground: #f2f2f2; + --vscode-editorLink-activeForeground: #0000ff; + --vscode-editorInlayHint-foreground: #57606a; + --vscode-editorInlayHint-background: rgba(175, 184, 193, 0.2); + --vscode-editorInlayHint-typeForeground: #57606a; + --vscode-editorInlayHint-typeBackground: rgba(175, 184, 193, 0.2); + --vscode-editorInlayHint-parameterForeground: #57606a; + --vscode-editorInlayHint-parameterBackground: rgba(175, 184, 193, 0.2); + --vscode-editorLightBulb-foreground: #ddb100; + --vscode-editorLightBulbAutoFix-foreground: #007acc; + --vscode-diffEditor-insertedTextBackground: rgba(111, 221, 139, 0.4); + --vscode-diffEditor-removedTextBackground: rgba(255, 171, 168, 0.4); + --vscode-diffEditor-insertedLineBackground: rgba(172, 238, 187, 0.3); + --vscode-diffEditor-removedLineBackground: rgba(255, 206, 203, 0.3); + --vscode-diffEditor-diagonalFill: rgba(34, 34, 34, 0.2); + --vscode-list-focusBackground: #ddf4ff; + --vscode-list-focusForeground: #24292f; + --vscode-list-focusOutline: #0969da; + --vscode-list-activeSelectionBackground: rgba(175, 184, 193, 0.2); + --vscode-list-activeSelectionForeground: #24292f; + --vscode-list-inactiveSelectionBackground: rgba(175, 184, 193, 0.2); + --vscode-list-inactiveSelectionForeground: #24292f; + --vscode-list-inactiveFocusBackground: #ddf4ff; + --vscode-list-hoverBackground: rgba(234, 238, 242, 0.5); + --vscode-list-hoverForeground: #24292f; + --vscode-list-dropBackground: #d6ebff; + --vscode-list-highlightForeground: #0969da; + --vscode-list-focusHighlightForeground: #0969da; + --vscode-list-invalidItemForeground: #b89500; + --vscode-list-errorForeground: #b01011; + --vscode-list-warningForeground: #855f00; + --vscode-listFilterWidget-background: #ffffff; + --vscode-listFilterWidget-outline: rgba(0, 0, 0, 0); + --vscode-listFilterWidget-noMatchesOutline: #be1100; + --vscode-listFilterWidget-shadow: rgba(0, 0, 0, 0.16); + --vscode-list-filterMatchBackground: rgba(250, 225, 125, 0.5); + --vscode-tree-indentGuidesStroke: #d8dee4; + --vscode-tree-tableColumnsBorder: rgba(97, 97, 97, 0.13); + --vscode-tree-tableOddRowsBackground: rgba(36, 41, 47, 0.04); + --vscode-list-deemphasizedForeground: #8e8e90; + --vscode-checkbox-background: #f6f8fa; + --vscode-checkbox-selectBackground: #ffffff; + --vscode-checkbox-foreground: #24292f; + --vscode-checkbox-border: #d0d7de; + --vscode-checkbox-selectBorder: #ffffff; + --vscode-quickInputList-focusForeground: #24292f; + --vscode-quickInputList-focusBackground: rgba(175, 184, 193, 0.2); + --vscode-menu-foreground: #24292f; + --vscode-menu-background: #ffffff; + --vscode-menu-selectionForeground: #24292f; + --vscode-menu-selectionBackground: rgba(175, 184, 193, 0.2); + --vscode-menu-separatorBackground: #d4d4d4; + --vscode-toolbar-hoverBackground: rgba(184, 184, 184, 0.31); + --vscode-toolbar-activeBackground: rgba(166, 166, 166, 0.31); + --vscode-editor-snippetTabstopHighlightBackground: rgba(10, 50, 100, 0.2); + --vscode-editor-snippetFinalTabstopHighlightBorder: rgba(10, 50, 100, 0.5); + --vscode-breadcrumb-foreground: #57606a; + --vscode-breadcrumb-background: #ffffff; + --vscode-breadcrumb-focusForeground: #24292f; + --vscode-breadcrumb-activeSelectionForeground: #57606a; + --vscode-breadcrumbPicker-background: #ffffff; + --vscode-merge-currentHeaderBackground: rgba(64, 200, 174, 0.5); + --vscode-merge-currentContentBackground: rgba(64, 200, 174, 0.2); + --vscode-merge-incomingHeaderBackground: rgba(64, 166, 255, 0.5); + --vscode-merge-incomingContentBackground: rgba(64, 166, 255, 0.2); + --vscode-merge-commonHeaderBackground: rgba(96, 96, 96, 0.4); + --vscode-merge-commonContentBackground: rgba(96, 96, 96, 0.16); + --vscode-editorOverviewRuler-currentContentForeground: rgba( + 64, + 200, + 174, + 0.5 + ); + --vscode-editorOverviewRuler-incomingContentForeground: rgba( + 64, + 166, + 255, + 0.5 + ); + --vscode-editorOverviewRuler-commonContentForeground: rgba(96, 96, 96, 0.4); + --vscode-editorOverviewRuler-findMatchForeground: rgba(209, 134, 22, 0.49); + --vscode-editorOverviewRuler-selectionHighlightForeground: rgba( + 160, + 160, + 160, + 0.8 + ); + --vscode-minimap-findMatchHighlight: #d18616; + --vscode-minimap-selectionOccurrenceHighlight: #c9c9c9; + --vscode-minimap-selectionHighlight: #add6ff; + --vscode-minimap-errorHighlight: rgba(255, 18, 18, 0.7); + --vscode-minimap-warningHighlight: #bf8803; + --vscode-minimap-foregroundOpacity: #000000; + --vscode-minimapSlider-background: rgba(140, 149, 159, 0.1); + --vscode-minimapSlider-hoverBackground: rgba(140, 149, 159, 0.14); + --vscode-minimapSlider-activeBackground: rgba(140, 149, 159, 0.27); + --vscode-problemsErrorIcon-foreground: #e51400; + --vscode-problemsWarningIcon-foreground: #bf8803; + --vscode-problemsInfoIcon-foreground: #1a85ff; + --vscode-charts-foreground: #24292f; + --vscode-charts-lines: rgba(36, 41, 47, 0.5); + --vscode-charts-red: #e51400; + --vscode-charts-blue: #1a85ff; + --vscode-charts-yellow: #bf8803; + --vscode-charts-orange: #d18616; + --vscode-charts-green: #388a34; + --vscode-charts-purple: #652d90; + --vscode-symbolIcon-arrayForeground: #953800; + --vscode-symbolIcon-booleanForeground: #0550ae; + --vscode-symbolIcon-classForeground: #953800; + --vscode-symbolIcon-colorForeground: #0a3069; + --vscode-symbolIcon-constantForeground: #116329; + --vscode-symbolIcon-constructorForeground: #3e1f79; + --vscode-symbolIcon-enumeratorForeground: #953800; + --vscode-symbolIcon-enumeratorMemberForeground: #0550ae; + --vscode-symbolIcon-eventForeground: #57606a; + --vscode-symbolIcon-fieldForeground: #953800; + --vscode-symbolIcon-fileForeground: #7d4e00; + --vscode-symbolIcon-folderForeground: #7d4e00; + --vscode-symbolIcon-functionForeground: #6639ba; + --vscode-symbolIcon-interfaceForeground: #953800; + --vscode-symbolIcon-keyForeground: #0550ae; + --vscode-symbolIcon-keywordForeground: #a40e26; + --vscode-symbolIcon-methodForeground: #6639ba; + --vscode-symbolIcon-moduleForeground: #a40e26; + --vscode-symbolIcon-namespaceForeground: #a40e26; + --vscode-symbolIcon-nullForeground: #0550ae; + --vscode-symbolIcon-numberForeground: #116329; + --vscode-symbolIcon-objectForeground: #953800; + --vscode-symbolIcon-operatorForeground: #0a3069; + --vscode-symbolIcon-packageForeground: #953800; + --vscode-symbolIcon-propertyForeground: #953800; + --vscode-symbolIcon-referenceForeground: #0550ae; + --vscode-symbolIcon-snippetForeground: #0550ae; + --vscode-symbolIcon-stringForeground: #0a3069; + --vscode-symbolIcon-structForeground: #953800; + --vscode-symbolIcon-textForeground: #0a3069; + --vscode-symbolIcon-typeParameterForeground: #0a3069; + --vscode-symbolIcon-unitForeground: #0550ae; + --vscode-symbolIcon-variableForeground: #953800; + --vscode-editor-lineHighlightBackground: rgba(234, 238, 242, 0.5); + --vscode-editor-lineHighlightBorder: #eeeeee; + --vscode-editor-rangeHighlightBackground: rgba(253, 255, 0, 0.2); + --vscode-editor-symbolHighlightBackground: rgba(250, 225, 125, 0.5); + --vscode-editorCursor-foreground: #0969da; + --vscode-editorWhitespace-foreground: #afb8c1; + --vscode-editorIndentGuide-background: rgba(36, 41, 47, 0.12); + --vscode-editorIndentGuide-activeBackground: rgba(36, 41, 47, 0.24); + --vscode-editorLineNumber-foreground: #8c959f; + --vscode-editorActiveLineNumber-foreground: #0b216f; + --vscode-editorLineNumber-activeForeground: #24292f; + --vscode-editorRuler-foreground: #d3d3d3; + --vscode-editorCodeLens-foreground: #919191; + --vscode-editorBracketMatch-background: rgba(74, 194, 107, 0.25); + --vscode-editorBracketMatch-border: rgba(74, 194, 107, 0.6); + --vscode-editorOverviewRuler-border: #ffffff; + --vscode-editorGutter-background: #ffffff; + --vscode-editorUnnecessaryCode-opacity: rgba(0, 0, 0, 0.47); + --vscode-editorGhostText-foreground: rgba(0, 0, 0, 0.47); + --vscode-editorOverviewRuler-rangeHighlightForeground: rgba(0, 122, 204, 0.6); + --vscode-editorOverviewRuler-errorForeground: rgba(255, 18, 18, 0.7); + --vscode-editorOverviewRuler-warningForeground: #bf8803; + --vscode-editorOverviewRuler-infoForeground: #1a85ff; + --vscode-editorBracketHighlight-foreground1: #0969da; + --vscode-editorBracketHighlight-foreground2: #1a7f37; + --vscode-editorBracketHighlight-foreground3: #9a6700; + --vscode-editorBracketHighlight-foreground4: #cf222e; + --vscode-editorBracketHighlight-foreground5: #bf3989; + --vscode-editorBracketHighlight-foreground6: #8250df; + --vscode-editorBracketHighlight-unexpectedBracket\.foreground: #57606a; + --vscode-editorBracketPairGuide-background1: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background2: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background3: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background4: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background5: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background6: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground1: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground2: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground3: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground4: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground5: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground6: rgba(0, 0, 0, 0); + --vscode-editorUnicodeHighlight-border: #cea33d; + --vscode-editorUnicodeHighlight-background: rgba(206, 163, 61, 0.08); + --vscode-editorHoverWidget-highlightForeground: #0969da; + --vscode-editorOverviewRuler-bracketMatchForeground: #a0a0a0; + --vscode-editor-foldBackground: rgba(110, 119, 129, 0.1); + --vscode-editorGutter-foldingControlForeground: #57606a; + --vscode-editor-linkedEditingBackground: rgba(9, 105, 218, 0.07); + --vscode-editor-wordHighlightBackground: rgba(234, 238, 242, 0.5); + --vscode-editor-wordHighlightStrongBackground: rgba(175, 184, 193, 0.3); + --vscode-editor-wordHighlightBorder: rgba(175, 184, 193, 0.6); + --vscode-editor-wordHighlightStrongBorder: rgba(175, 184, 193, 0.6); + --vscode-editorOverviewRuler-wordHighlightForeground: rgba( + 160, + 160, + 160, + 0.8 + ); + --vscode-editorOverviewRuler-wordHighlightStrongForeground: rgba( + 192, + 160, + 192, + 0.8 + ); + --vscode-peekViewTitle-background: rgba(26, 133, 255, 0.1); + --vscode-peekViewTitleLabel-foreground: #000000; + --vscode-peekViewTitleDescription-foreground: #616161; + --vscode-peekView-border: #1a85ff; + --vscode-peekViewResult-background: #f3f3f3; + --vscode-peekViewResult-lineForeground: #646465; + --vscode-peekViewResult-fileForeground: #1e1e1e; + --vscode-peekViewResult-selectionBackground: rgba(51, 153, 255, 0.2); + --vscode-peekViewResult-selectionForeground: #6c6c6c; + --vscode-peekViewEditor-background: #f2f8fc; + --vscode-peekViewEditorGutter-background: #f2f8fc; + --vscode-peekViewResult-matchHighlightBackground: rgba(234, 92, 0, 0.3); + --vscode-peekViewEditor-matchHighlightBackground: rgba(245, 216, 2, 0.87); + --vscode-editorMarkerNavigationError-background: #e51400; + --vscode-editorMarkerNavigationError-headerBackground: rgba(229, 20, 0, 0.1); + --vscode-editorMarkerNavigationWarning-background: #bf8803; + --vscode-editorMarkerNavigationWarning-headerBackground: rgba( + 191, + 136, + 3, + 0.1 + ); + --vscode-editorMarkerNavigationInfo-background: #1a85ff; + --vscode-editorMarkerNavigationInfo-headerBackground: rgba(26, 133, 255, 0.1); + --vscode-editorMarkerNavigation-background: #ffffff; + --vscode-editorSuggestWidget-background: #ffffff; + --vscode-editorSuggestWidget-border: #c8c8c8; + --vscode-editorSuggestWidget-foreground: #24292f; + --vscode-editorSuggestWidget-selectedForeground: #24292f; + --vscode-editorSuggestWidget-selectedBackground: rgba(175, 184, 193, 0.2); + --vscode-editorSuggestWidget-highlightForeground: #0969da; + --vscode-editorSuggestWidget-focusHighlightForeground: #0969da; + --vscode-editorSuggestWidgetStatus-foreground: rgba(36, 41, 47, 0.5); + --vscode-tab-activeBackground: #ffffff; + --vscode-tab-unfocusedActiveBackground: #ffffff; + --vscode-tab-inactiveBackground: #f6f8fa; + --vscode-tab-unfocusedInactiveBackground: #f6f8fa; + --vscode-tab-activeForeground: #24292f; + --vscode-tab-inactiveForeground: #57606a; + --vscode-tab-unfocusedActiveForeground: rgba(36, 41, 47, 0.7); + --vscode-tab-unfocusedInactiveForeground: rgba(87, 96, 106, 0.5); + --vscode-tab-hoverBackground: #ffffff; + --vscode-tab-unfocusedHoverBackground: rgba(234, 238, 242, 0.5); + --vscode-tab-border: #d0d7de; + --vscode-tab-lastPinnedBorder: #d8dee4; + --vscode-tab-activeBorder: #ffffff; + --vscode-tab-unfocusedActiveBorder: #ffffff; + --vscode-tab-activeBorderTop: #fd8c73; + --vscode-tab-unfocusedActiveBorderTop: #d0d7de; + --vscode-tab-activeModifiedBorder: #33aaee; + --vscode-tab-inactiveModifiedBorder: rgba(51, 170, 238, 0.5); + --vscode-tab-unfocusedActiveModifiedBorder: rgba(51, 170, 238, 0.7); + --vscode-tab-unfocusedInactiveModifiedBorder: rgba(51, 170, 238, 0.25); + --vscode-editorPane-background: #ffffff; + --vscode-editorGroupHeader-tabsBackground: #f6f8fa; + --vscode-editorGroupHeader-tabsBorder: #d0d7de; + --vscode-editorGroupHeader-noTabsBackground: #ffffff; + --vscode-editorGroup-border: #d0d7de; + --vscode-editorGroup-dropBackground: rgba(38, 119, 203, 0.18); + --vscode-editorGroup-dropIntoPromptForeground: #24292f; + --vscode-editorGroup-dropIntoPromptBackground: #ffffff; + --vscode-sideBySideEditor-horizontalBorder: #d0d7de; + --vscode-sideBySideEditor-verticalBorder: #d0d7de; + --vscode-panel-background: #f6f8fa; + --vscode-panel-border: #d0d7de; + --vscode-panelTitle-activeForeground: #24292f; + --vscode-panelTitle-inactiveForeground: #57606a; + --vscode-panelTitle-activeBorder: #fd8c73; + --vscode-panelInput-border: #d0d7de; + --vscode-panel-dropBorder: #24292f; + --vscode-panelSection-dropBackground: rgba(38, 119, 203, 0.18); + --vscode-panelSectionHeader-background: rgba(128, 128, 128, 0.2); + --vscode-panelSection-border: #d0d7de; + --vscode-banner-background: rgba(113, 129, 145, 0.2); + --vscode-banner-foreground: #24292f; + --vscode-banner-iconForeground: #1a85ff; + --vscode-statusBar-foreground: #57606a; + --vscode-statusBar-noFolderForeground: #57606a; + --vscode-statusBar-background: #ffffff; + --vscode-statusBar-noFolderBackground: #ffffff; + --vscode-statusBar-border: #d0d7de; + --vscode-statusBar-focusBorder: rgba(9, 105, 218, 0.5); + --vscode-statusBar-noFolderBorder: #d0d7de; + --vscode-statusBarItem-activeBackground: rgba(36, 41, 47, 0.12); + --vscode-statusBarItem-focusBorder: #0969da; + --vscode-statusBarItem-hoverBackground: rgba(36, 41, 47, 0.08); + --vscode-statusBarItem-compactHoverBackground: rgba(255, 255, 255, 0.2); + --vscode-statusBarItem-prominentForeground: #57606a; + --vscode-statusBarItem-prominentBackground: rgba(175, 184, 193, 0.2); + --vscode-statusBarItem-prominentHoverBackground: rgba(0, 0, 0, 0.3); + --vscode-statusBarItem-errorBackground: #7c141b; + --vscode-statusBarItem-errorForeground: #ffffff; + --vscode-statusBarItem-warningBackground: #725102; + --vscode-statusBarItem-warningForeground: #ffffff; + --vscode-activityBar-background: #ffffff; + --vscode-activityBar-foreground: #24292f; + --vscode-activityBar-inactiveForeground: #57606a; + --vscode-activityBar-border: #d0d7de; + --vscode-activityBar-activeBorder: #fd8c73; + --vscode-activityBar-dropBorder: #24292f; + --vscode-activityBarBadge-background: #0969da; + --vscode-activityBarBadge-foreground: #ffffff; + --vscode-activityBarItem-profilesForeground: #57606a; + --vscode-activityBarItem-profilesHoverForeground: #24292f; + --vscode-activityBarItem-profilesBackground: #e0e0e0; + --vscode-statusBarItem-remoteBackground: #eaeef2; + --vscode-statusBarItem-remoteForeground: #24292f; + --vscode-extensionBadge-remoteBackground: #0969da; + --vscode-extensionBadge-remoteForeground: #ffffff; + --vscode-sideBar-background: #f6f8fa; + --vscode-sideBar-foreground: #24292f; + --vscode-sideBar-border: #d0d7de; + --vscode-sideBarTitle-foreground: #24292f; + --vscode-sideBar-dropBackground: rgba(38, 119, 203, 0.18); + --vscode-sideBarSectionHeader-background: #f6f8fa; + --vscode-sideBarSectionHeader-foreground: #24292f; + --vscode-sideBarSectionHeader-border: #d0d7de; + --vscode-titleBar-activeForeground: #57606a; + --vscode-titleBar-inactiveForeground: #57606a; + --vscode-titleBar-activeBackground: #ffffff; + --vscode-titleBar-inactiveBackground: #f6f8fa; + --vscode-titleBar-border: #d0d7de; + --vscode-menubar-selectionForeground: #57606a; + --vscode-menubar-selectionBackground: rgba(184, 184, 184, 0.31); + --vscode-notifications-foreground: #24292f; + --vscode-notifications-background: #ffffff; + --vscode-notificationLink-foreground: #0969da; + --vscode-notificationCenterHeader-foreground: #57606a; + --vscode-notificationCenterHeader-background: #f6f8fa; + --vscode-notifications-border: #d0d7de; + --vscode-notificationsErrorIcon-foreground: #cf222e; + --vscode-notificationsWarningIcon-foreground: #9a6700; + --vscode-notificationsInfoIcon-foreground: #0969da; + --vscode-commandCenter-foreground: #57606a; + --vscode-commandCenter-activeForeground: #57606a; + --vscode-commandCenter-inactiveForeground: #57606a; + --vscode-commandCenter-background: rgba(0, 0, 0, 0.05); + --vscode-commandCenter-activeBackground: rgba(0, 0, 0, 0.08); + --vscode-commandCenter-border: rgba(87, 96, 106, 0.2); + --vscode-commandCenter-activeBorder: rgba(87, 96, 106, 0.3); + --vscode-commandCenter-inactiveBorder: rgba(87, 96, 106, 0.25); + --vscode-editorCommentsWidget-resolvedBorder: rgba(97, 97, 97, 0.5); + --vscode-editorCommentsWidget-unresolvedBorder: #1a85ff; + --vscode-editorCommentsWidget-rangeBackground: rgba(26, 133, 255, 0.1); + --vscode-editorCommentsWidget-rangeBorder: rgba(26, 133, 255, 0.4); + --vscode-editorCommentsWidget-rangeActiveBackground: rgba(26, 133, 255, 0.1); + --vscode-editorCommentsWidget-rangeActiveBorder: rgba(26, 133, 255, 0.4); + --vscode-editorGutter-commentRangeForeground: rgba(165, 175, 185, 0.2); + --vscode-debugToolBar-background: #ffffff; + --vscode-debugIcon-startForeground: #388a34; + --vscode-editor-stackFrameHighlightBackground: rgba(212, 167, 44, 0.4); + --vscode-editor-focusedStackFrameHighlightBackground: rgba(74, 194, 107, 0.4); + --vscode-mergeEditor-change\.background: rgba(155, 185, 85, 0.2); + --vscode-mergeEditor-change\.word\.background: rgba(156, 204, 44, 0.4); + --vscode-mergeEditor-changeBase\.background: #ffcccc; + --vscode-mergeEditor-changeBase\.word\.background: #ffa3a3; + --vscode-mergeEditor-conflict\.unhandledUnfocused\.border: #ffa600; + --vscode-mergeEditor-conflict\.unhandledFocused\.border: #ffa600; + --vscode-mergeEditor-conflict\.handledUnfocused\.border: rgba( + 134, + 134, + 134, + 0.29 + ); + --vscode-mergeEditor-conflict\.handledFocused\.border: rgba( + 193, + 193, + 193, + 0.8 + ); + --vscode-mergeEditor-conflict\.handled\.minimapOverViewRuler: rgba( + 173, + 172, + 168, + 0.93 + ); + --vscode-mergeEditor-conflict\.unhandled\.minimapOverViewRuler: #fcba03; + --vscode-mergeEditor-conflictingLines\.background: rgba(255, 234, 0, 0.28); + --vscode-mergeEditor-conflict\.input1\.background: rgba(64, 200, 174, 0.2); + --vscode-mergeEditor-conflict\.input2\.background: rgba(64, 166, 255, 0.2); + --vscode-settings-headerForeground: #57606a; + --vscode-settings-modifiedItemIndicator: rgba(212, 167, 44, 0.4); + --vscode-settings-headerBorder: #d0d7de; + --vscode-settings-sashBorder: #d0d7de; + --vscode-settings-dropdownBackground: #ffffff; + --vscode-settings-dropdownForeground: #24292f; + --vscode-settings-dropdownBorder: #d0d7de; + --vscode-settings-dropdownListBorder: #c8c8c8; + --vscode-settings-checkboxBackground: #f6f8fa; + --vscode-settings-checkboxForeground: #24292f; + --vscode-settings-checkboxBorder: #d0d7de; + --vscode-settings-textInputBackground: #ffffff; + --vscode-settings-textInputForeground: #24292f; + --vscode-settings-textInputBorder: #d0d7de; + --vscode-settings-numberInputBackground: #ffffff; + --vscode-settings-numberInputForeground: #24292f; + --vscode-settings-numberInputBorder: #d0d7de; + --vscode-settings-focusedRowBackground: rgba(234, 238, 242, 0.3); + --vscode-settings-rowHoverBackground: rgba(234, 238, 242, 0.15); + --vscode-settings-focusedRowBorder: #0969da; + --vscode-terminal-foreground: #24292f; + --vscode-terminal-selectionBackground: #add6ff; + --vscode-terminal-inactiveSelectionBackground: rgba(173, 214, 255, 0.5); + --vscode-terminalCommandDecoration-defaultBackground: rgba(0, 0, 0, 0.25); + --vscode-terminalCommandDecoration-successBackground: #2090d3; + --vscode-terminalCommandDecoration-errorBackground: #e51400; + --vscode-terminalOverviewRuler-cursorForeground: rgba(160, 160, 160, 0.8); + --vscode-terminal-border: #d0d7de; + --vscode-terminal-findMatchBackground: #bf8700; + --vscode-terminal-findMatchHighlightBackground: rgba(250, 225, 125, 0.5); + --vscode-terminalOverviewRuler-findMatchForeground: rgba(209, 134, 22, 0.49); + --vscode-terminal-dropBackground: rgba(38, 119, 203, 0.18); + --vscode-terminal-tab\.activeBorder: #ffffff; + --vscode-testing-iconFailed: #f14c4c; + --vscode-testing-iconErrored: #f14c4c; + --vscode-testing-iconPassed: #73c991; + --vscode-testing-runAction: #73c991; + --vscode-testing-iconQueued: #cca700; + --vscode-testing-iconUnset: #848484; + --vscode-testing-iconSkipped: #848484; + --vscode-testing-peekBorder: #e51400; + --vscode-testing-peekHeaderBackground: rgba(229, 20, 0, 0.1); + --vscode-testing-message\.error\.decorationForeground: #e51400; + --vscode-testing-message\.error\.lineBackground: rgba(255, 0, 0, 0.2); + --vscode-testing-message\.info\.decorationForeground: rgba(36, 41, 47, 0.5); + --vscode-welcomePage-tileBackground: #ffffff; + --vscode-welcomePage-tileHoverBackground: #e6e6e6; + --vscode-welcomePage-tileBorder: rgba(0, 0, 0, 0.1); + --vscode-welcomePage-progress\.background: #ffffff; + --vscode-welcomePage-progress\.foreground: #0969da; + --vscode-walkthrough-stepTitle\.foreground: #000000; + --vscode-debugExceptionWidget-border: #a31515; + --vscode-debugExceptionWidget-background: #f1dfde; + --vscode-ports-iconRunningProcessForeground: #eaeef2; + --vscode-statusBar-debuggingBackground: #cf222e; + --vscode-statusBar-debuggingForeground: #ffffff; + --vscode-statusBar-debuggingBorder: #d0d7de; + --vscode-editor-inlineValuesForeground: rgba(0, 0, 0, 0.5); + --vscode-editor-inlineValuesBackground: rgba(255, 200, 0, 0.2); + --vscode-editorGutter-modifiedBackground: rgba(212, 167, 44, 0.4); + --vscode-editorGutter-addedBackground: rgba(74, 194, 107, 0.4); + --vscode-editorGutter-deletedBackground: rgba(255, 129, 130, 0.4); + --vscode-minimapGutter-modifiedBackground: rgba(212, 167, 44, 0.4); + --vscode-minimapGutter-addedBackground: rgba(74, 194, 107, 0.4); + --vscode-minimapGutter-deletedBackground: rgba(255, 129, 130, 0.4); + --vscode-editorOverviewRuler-modifiedForeground: rgba(212, 167, 44, 0.24); + --vscode-editorOverviewRuler-addedForeground: rgba(74, 194, 107, 0.24); + --vscode-editorOverviewRuler-deletedForeground: rgba(255, 129, 130, 0.24); + --vscode-debugIcon-breakpointForeground: #cf222e; + --vscode-debugIcon-breakpointDisabledForeground: #848484; + --vscode-debugIcon-breakpointUnverifiedForeground: #848484; + --vscode-debugIcon-breakpointCurrentStackframeForeground: #be8700; + --vscode-debugIcon-breakpointStackframeForeground: #89d185; + --vscode-notebook-cellBorderColor: rgba(175, 184, 193, 0.2); + --vscode-notebook-focusedEditorBorder: #0969da; + --vscode-notebookStatusSuccessIcon-foreground: #388a34; + --vscode-notebookStatusErrorIcon-foreground: #cf222e; + --vscode-notebookStatusRunningIcon-foreground: #24292f; + --vscode-notebook-cellToolbarSeparator: rgba(128, 128, 128, 0.35); + --vscode-notebook-selectedCellBackground: rgba(175, 184, 193, 0.2); + --vscode-notebook-selectedCellBorder: rgba(175, 184, 193, 0.2); + --vscode-notebook-focusedCellBorder: #0969da; + --vscode-notebook-inactiveFocusedCellBorder: rgba(175, 184, 193, 0.2); + --vscode-notebook-cellStatusBarItemHoverBackground: rgba(0, 0, 0, 0.08); + --vscode-notebook-cellInsertionIndicator: #0969da; + --vscode-notebookScrollbarSlider-background: rgba(140, 149, 159, 0.2); + --vscode-notebookScrollbarSlider-hoverBackground: rgba(140, 149, 159, 0.27); + --vscode-notebookScrollbarSlider-activeBackground: rgba(140, 149, 159, 0.53); + --vscode-notebook-symbolHighlightBackground: rgba(253, 255, 0, 0.2); + --vscode-notebook-cellEditorBackground: #f6f8fa; + --vscode-notebook-editorBackground: #ffffff; + --vscode-keybindingTable-headerBackground: rgba(36, 41, 47, 0.04); + --vscode-keybindingTable-rowsBackground: rgba(36, 41, 47, 0.04); + --vscode-scm-providerBorder: #c8c8c8; + --vscode-searchEditor-textInputBorder: #d0d7de; + --vscode-debugTokenExpression-name: #0550ae; + --vscode-debugTokenExpression-value: #0a3069; + --vscode-debugTokenExpression-string: #0a3069; + --vscode-debugTokenExpression-boolean: #116329; + --vscode-debugTokenExpression-number: #116329; + --vscode-debugTokenExpression-error: #a40e26; + --vscode-debugView-exceptionLabelForeground: #ffffff; + --vscode-debugView-exceptionLabelBackground: #a31515; + --vscode-debugView-stateLabelForeground: #24292f; + --vscode-debugView-stateLabelBackground: rgba(136, 136, 136, 0.27); + --vscode-debugView-valueChangedHighlight: #569cd6; + --vscode-debugConsole-infoForeground: #57606a; + --vscode-debugConsole-warningForeground: #7d4e00; + --vscode-debugConsole-errorForeground: #cf222e; + --vscode-debugConsole-sourceForeground: #9a6700; + --vscode-debugConsoleInputIcon-foreground: #6639ba; + --vscode-debugIcon-pauseForeground: #007acc; + --vscode-debugIcon-stopForeground: #a1260d; + --vscode-debugIcon-disconnectForeground: #a1260d; + --vscode-debugIcon-restartForeground: #388a34; + --vscode-debugIcon-stepOverForeground: #007acc; + --vscode-debugIcon-stepIntoForeground: #007acc; + --vscode-debugIcon-stepOutForeground: #007acc; + --vscode-debugIcon-continueForeground: #007acc; + --vscode-debugIcon-stepBackForeground: #007acc; + --vscode-extensionButton-background: #2da44e; + --vscode-extensionButton-foreground: #ffffff; + --vscode-extensionButton-hoverBackground: #2c974b; + --vscode-extensionButton-separator: rgba(255, 255, 255, 0.4); + --vscode-extensionButton-prominentBackground: #2da44e; + --vscode-extensionButton-prominentForeground: #ffffff; + --vscode-extensionButton-prominentHoverBackground: #2c974b; + --vscode-extensionIcon-starForeground: #df6100; + --vscode-extensionIcon-verifiedForeground: #0969da; + --vscode-extensionIcon-preReleaseForeground: #1d9271; + --vscode-extensionIcon-sponsorForeground: #b51e78; + --vscode-terminal-ansiBlack: #24292f; + --vscode-terminal-ansiRed: #cf222e; + --vscode-terminal-ansiGreen: #116329; + --vscode-terminal-ansiYellow: #4d2d00; + --vscode-terminal-ansiBlue: #0969da; + --vscode-terminal-ansiMagenta: #8250df; + --vscode-terminal-ansiCyan: #1b7c83; + --vscode-terminal-ansiWhite: #6e7781; + --vscode-terminal-ansiBrightBlack: #57606a; + --vscode-terminal-ansiBrightRed: #a40e26; + --vscode-terminal-ansiBrightGreen: #1a7f37; + --vscode-terminal-ansiBrightYellow: #633c01; + --vscode-terminal-ansiBrightBlue: #218bff; + --vscode-terminal-ansiBrightMagenta: #a475f9; + --vscode-terminal-ansiBrightCyan: #3192aa; + --vscode-terminal-ansiBrightWhite: #8c959f; + --vscode-interactive-activeCodeBorder: #1a85ff; + --vscode-interactive-inactiveCodeBorder: rgba(175, 184, 193, 0.2); + --vscode-gitDecoration-addedResourceForeground: #1a7f37; + --vscode-gitDecoration-modifiedResourceForeground: #9a6700; + --vscode-gitDecoration-deletedResourceForeground: #cf222e; + --vscode-gitDecoration-renamedResourceForeground: #007100; + --vscode-gitDecoration-untrackedResourceForeground: #1a7f37; + --vscode-gitDecoration-ignoredResourceForeground: #6e7781; + --vscode-gitDecoration-stageModifiedResourceForeground: #895503; + --vscode-gitDecoration-stageDeletedResourceForeground: #ad0707; + --vscode-gitDecoration-conflictingResourceForeground: #bc4c00; + --vscode-gitDecoration-submoduleResourceForeground: #57606a; + --vscode-testExplorer-errorDecorationBackground: #f2dede; +} + +/** + * This is copied in the same way, but from the element + */ +body { + background-color: transparent; + color: var(--vscode-editor-foreground); + font-family: var(--vscode-font-family); + font-weight: var(--vscode-font-weight); + font-size: var(--vscode-font-size); + margin: 0; + padding: 0 20px; +} + +/** + * This is used for setting the background on the Storybook preview. + */ +body { + background-color: var(--vscode-editor-background); +} diff --git a/extensions/ql-vscode/src/stories/vscode-theme-light-high-contrast.css b/extensions/ql-vscode/src/stories/vscode-theme-light-high-contrast.css new file mode 100644 index 000000000..0899173e3 --- /dev/null +++ b/extensions/ql-vscode/src/stories/vscode-theme-light-high-contrast.css @@ -0,0 +1,596 @@ +/* + * These were copied from VSCode Light High Contrast theme. + * + * To update these, open a webview in VSCode, open the webview developer tools and find the + * iframe hosting the webview. The element will have a style attribute that contains + * the CSS variables. Copy these to this file. + */ +:root { + --vscode-font-family: -apple-system, BlinkMacSystemFont, sans-serif; + --vscode-font-weight: normal; + --vscode-font-size: 13px; + --vscode-editor-font-family: Menlo, Monaco, "Courier New", monospace; + --vscode-editor-font-weight: normal; + --vscode-editor-font-size: 12px; + --vscode-foreground: #292929; + --vscode-disabledForeground: #7f7f7f; + --vscode-errorForeground: #b5200d; + --vscode-descriptionForeground: rgba(41, 41, 41, 0.7); + --vscode-icon-foreground: #292929; + --vscode-focusBorder: #0f4a85; + --vscode-contrastBorder: #0f4a85; + --vscode-contrastActiveBorder: #0f4a85; + --vscode-textSeparator-foreground: #292929; + --vscode-textLink-foreground: #0f4a85; + --vscode-textLink-activeForeground: #0f4a85; + --vscode-textPreformat-foreground: #292929; + --vscode-textBlockQuote-background: #f2f2f2; + --vscode-textBlockQuote-border: #292929; + --vscode-textCodeBlock-background: #f2f2f2; + --vscode-input-background: #ffffff; + --vscode-input-foreground: #292929; + --vscode-input-border: #0f4a85; + --vscode-inputOption-activeBorder: #0f4a85; + --vscode-inputOption-activeBackground: rgba(0, 0, 0, 0); + --vscode-inputOption-activeForeground: #292929; + --vscode-input-placeholderForeground: rgba(41, 41, 41, 0.7); + --vscode-inputValidation-infoBackground: #ffffff; + --vscode-inputValidation-infoForeground: #292929; + --vscode-inputValidation-infoBorder: #0f4a85; + --vscode-inputValidation-warningBackground: #ffffff; + --vscode-inputValidation-warningForeground: #292929; + --vscode-inputValidation-warningBorder: #0f4a85; + --vscode-inputValidation-errorBackground: #ffffff; + --vscode-inputValidation-errorForeground: #292929; + --vscode-inputValidation-errorBorder: #0f4a85; + --vscode-dropdown-background: #ffffff; + --vscode-dropdown-listBackground: #ffffff; + --vscode-dropdown-foreground: #292929; + --vscode-dropdown-border: #0f4a85; + --vscode-button-foreground: #ffffff; + --vscode-button-separator: rgba(255, 255, 255, 0.4); + --vscode-button-background: #0f4a85; + --vscode-button-hoverBackground: #0f4a85; + --vscode-button-border: #0f4a85; + --vscode-button-secondaryForeground: #292929; + --vscode-button-secondaryBackground: #ffffff; + --vscode-badge-background: #0f4a85; + --vscode-badge-foreground: #ffffff; + --vscode-scrollbarSlider-background: rgba(15, 74, 133, 0.4); + --vscode-scrollbarSlider-hoverBackground: rgba(15, 74, 133, 0.8); + --vscode-scrollbarSlider-activeBackground: #0f4a85; + --vscode-progressBar-background: #0f4a85; + --vscode-editorError-foreground: #b5200d; + --vscode-editorError-border: #b5200d; + --vscode-editorWarning-foreground: #895503; + --vscode-editorWarning-border: #ff0000; + --vscode-editorInfo-foreground: #1a85ff; + --vscode-editorInfo-border: #292929; + --vscode-editorHint-border: #292929; + --vscode-sash-hoverBorder: #0f4a85; + --vscode-editor-background: #ffffff; + --vscode-editor-foreground: #292929; + --vscode-editorStickyScroll-background: #ffffff; + --vscode-editorStickyScrollHover-background: rgba(15, 74, 133, 0.1); + --vscode-editorWidget-background: #ffffff; + --vscode-editorWidget-foreground: #292929; + --vscode-editorWidget-border: #0f4a85; + --vscode-quickInput-background: #ffffff; + --vscode-quickInput-foreground: #292929; + --vscode-quickInputTitle-background: #ffffff; + --vscode-pickerGroup-foreground: #0f4a85; + --vscode-pickerGroup-border: #0f4a85; + --vscode-keybindingLabel-background: rgba(0, 0, 0, 0); + --vscode-keybindingLabel-foreground: #292929; + --vscode-keybindingLabel-border: #0f4a85; + --vscode-keybindingLabel-bottomBorder: #292929; + --vscode-editor-selectionBackground: #0f4a85; + --vscode-editor-selectionForeground: #ffffff; + --vscode-editor-inactiveSelectionBackground: rgba(15, 74, 133, 0.5); + --vscode-editor-selectionHighlightBorder: #0f4a85; + --vscode-editor-findMatchBorder: #0f4a85; + --vscode-editor-findMatchHighlightBorder: #0f4a85; + --vscode-editor-findRangeHighlightBorder: rgba(15, 74, 133, 0.4); + --vscode-searchEditor-findMatchBorder: #0f4a85; + --vscode-editorHoverWidget-background: #ffffff; + --vscode-editorHoverWidget-foreground: #292929; + --vscode-editorHoverWidget-border: #0f4a85; + --vscode-editorHoverWidget-statusBarBackground: #ffffff; + --vscode-editorLink-activeForeground: #292929; + --vscode-editorInlayHint-foreground: #ffffff; + --vscode-editorInlayHint-background: #0f4a85; + --vscode-editorInlayHint-typeForeground: #ffffff; + --vscode-editorInlayHint-typeBackground: #0f4a85; + --vscode-editorInlayHint-parameterForeground: #ffffff; + --vscode-editorInlayHint-parameterBackground: #0f4a85; + --vscode-editorLightBulb-foreground: #007acc; + --vscode-editorLightBulbAutoFix-foreground: #007acc; + --vscode-diffEditor-insertedTextBorder: #374e06; + --vscode-diffEditor-removedTextBorder: #ad0707; + --vscode-diffEditor-border: #0f4a85; + --vscode-list-focusOutline: #0f4a85; + --vscode-list-activeSelectionBackground: rgba(15, 74, 133, 0.1); + --vscode-list-inactiveSelectionBackground: rgba(15, 74, 133, 0.1); + --vscode-list-hoverBackground: rgba(15, 74, 133, 0.1); + --vscode-list-highlightForeground: #0f4a85; + --vscode-list-focusHighlightForeground: #0f4a85; + --vscode-list-invalidItemForeground: #b5200d; + --vscode-listFilterWidget-background: #ffffff; + --vscode-listFilterWidget-outline: #007acc; + --vscode-listFilterWidget-noMatchesOutline: #0f4a85; + --vscode-list-filterMatchBorder: #0f4a85; + --vscode-tree-indentGuidesStroke: #a5a5a5; + --vscode-list-deemphasizedForeground: #666666; + --vscode-checkbox-background: #ffffff; + --vscode-checkbox-selectBackground: #ffffff; + --vscode-checkbox-foreground: #292929; + --vscode-checkbox-border: #0f4a85; + --vscode-checkbox-selectBorder: #ffffff; + --vscode-menu-border: #0f4a85; + --vscode-menu-foreground: #292929; + --vscode-menu-background: #ffffff; + --vscode-menu-selectionBackground: rgba(15, 74, 133, 0.1); + --vscode-menu-selectionBorder: #0f4a85; + --vscode-menu-separatorBackground: #0f4a85; + --vscode-toolbar-hoverOutline: #0f4a85; + --vscode-editor-snippetTabstopHighlightBackground: rgba(10, 50, 100, 0.2); + --vscode-editor-snippetFinalTabstopHighlightBorder: #292929; + --vscode-breadcrumb-foreground: rgba(41, 41, 41, 0.8); + --vscode-breadcrumb-background: #ffffff; + --vscode-breadcrumb-focusForeground: #2d2d2d; + --vscode-breadcrumb-activeSelectionForeground: #2d2d2d; + --vscode-breadcrumbPicker-background: #ffffff; + --vscode-merge-border: #007acc; + --vscode-editorOverviewRuler-currentContentForeground: #007acc; + --vscode-editorOverviewRuler-incomingContentForeground: #007acc; + --vscode-editorOverviewRuler-commonContentForeground: #007acc; + --vscode-editorOverviewRuler-selectionHighlightForeground: rgba( + 160, + 160, + 160, + 0.8 + ); + --vscode-minimap-findMatchHighlight: #0f4a85; + --vscode-minimap-selectionOccurrenceHighlight: #0f4a85; + --vscode-minimap-selectionHighlight: #0f4a85; + --vscode-minimap-errorHighlight: #b5200d; + --vscode-minimap-warningHighlight: #ff0000; + --vscode-minimap-foregroundOpacity: #000000; + --vscode-minimapSlider-background: rgba(15, 74, 133, 0.2); + --vscode-minimapSlider-hoverBackground: rgba(15, 74, 133, 0.4); + --vscode-minimapSlider-activeBackground: rgba(15, 74, 133, 0.5); + --vscode-problemsErrorIcon-foreground: #b5200d; + --vscode-problemsWarningIcon-foreground: #895503; + --vscode-problemsInfoIcon-foreground: #1a85ff; + --vscode-charts-foreground: #292929; + --vscode-charts-lines: rgba(41, 41, 41, 0.5); + --vscode-charts-red: #b5200d; + --vscode-charts-blue: #1a85ff; + --vscode-charts-yellow: #895503; + --vscode-charts-orange: #0f4a85; + --vscode-charts-green: #374e06; + --vscode-charts-purple: #652d90; + --vscode-symbolIcon-arrayForeground: #292929; + --vscode-symbolIcon-booleanForeground: #292929; + --vscode-symbolIcon-classForeground: #d67e00; + --vscode-symbolIcon-colorForeground: #292929; + --vscode-symbolIcon-constantForeground: #292929; + --vscode-symbolIcon-constructorForeground: #652d90; + --vscode-symbolIcon-enumeratorForeground: #d67e00; + --vscode-symbolIcon-enumeratorMemberForeground: #007acc; + --vscode-symbolIcon-eventForeground: #d67e00; + --vscode-symbolIcon-fieldForeground: #007acc; + --vscode-symbolIcon-fileForeground: #292929; + --vscode-symbolIcon-folderForeground: #292929; + --vscode-symbolIcon-functionForeground: #652d90; + --vscode-symbolIcon-interfaceForeground: #007acc; + --vscode-symbolIcon-keyForeground: #292929; + --vscode-symbolIcon-keywordForeground: #292929; + --vscode-symbolIcon-methodForeground: #652d90; + --vscode-symbolIcon-moduleForeground: #292929; + --vscode-symbolIcon-namespaceForeground: #292929; + --vscode-symbolIcon-nullForeground: #292929; + --vscode-symbolIcon-numberForeground: #292929; + --vscode-symbolIcon-objectForeground: #292929; + --vscode-symbolIcon-operatorForeground: #292929; + --vscode-symbolIcon-packageForeground: #292929; + --vscode-symbolIcon-propertyForeground: #292929; + --vscode-symbolIcon-referenceForeground: #292929; + --vscode-symbolIcon-snippetForeground: #292929; + --vscode-symbolIcon-stringForeground: #292929; + --vscode-symbolIcon-structForeground: #292929; + --vscode-symbolIcon-textForeground: #292929; + --vscode-symbolIcon-typeParameterForeground: #292929; + --vscode-symbolIcon-unitForeground: #292929; + --vscode-symbolIcon-variableForeground: #007acc; + --vscode-editor-lineHighlightBorder: #0f4a85; + --vscode-editor-rangeHighlightBorder: #0f4a85; + --vscode-editor-symbolHighlightBorder: #0f4a85; + --vscode-editorCursor-foreground: #0f4a85; + --vscode-editorWhitespace-foreground: #cccccc; + --vscode-editorIndentGuide-background: #cccccc; + --vscode-editorIndentGuide-activeBackground: #cccccc; + --vscode-editorLineNumber-foreground: #292929; + --vscode-editorActiveLineNumber-foreground: #0f4a85; + --vscode-editorLineNumber-activeForeground: #0f4a85; + --vscode-editorRuler-foreground: #292929; + --vscode-editorCodeLens-foreground: #292929; + --vscode-editorBracketMatch-background: rgba(0, 0, 0, 0); + --vscode-editorBracketMatch-border: #0f4a85; + --vscode-editorOverviewRuler-border: #666666; + --vscode-editorGutter-background: #ffffff; + --vscode-editorUnnecessaryCode-border: #0f4a85; + --vscode-editorGhostText-border: rgba(41, 41, 41, 0.8); + --vscode-editorOverviewRuler-rangeHighlightForeground: rgba(0, 122, 204, 0.6); + --vscode-editorOverviewRuler-errorForeground: #b5200d; + --vscode-editorOverviewRuler-warningForeground: #ff0000; + --vscode-editorOverviewRuler-infoForeground: #292929; + --vscode-editorBracketHighlight-foreground1: #0431fa; + --vscode-editorBracketHighlight-foreground2: #319331; + --vscode-editorBracketHighlight-foreground3: #7b3814; + --vscode-editorBracketHighlight-foreground4: rgba(0, 0, 0, 0); + --vscode-editorBracketHighlight-foreground5: rgba(0, 0, 0, 0); + --vscode-editorBracketHighlight-foreground6: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background1: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background2: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background3: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background4: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background5: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-background6: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground1: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground2: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground3: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground4: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground5: rgba(0, 0, 0, 0); + --vscode-editorBracketPairGuide-activeBackground6: rgba(0, 0, 0, 0); + --vscode-editorHoverWidget-highlightForeground: #0f4a85; + --vscode-editorOverviewRuler-bracketMatchForeground: #a0a0a0; + --vscode-editorGutter-foldingControlForeground: #292929; + --vscode-editor-linkedEditingBackground: #ffffff; + --vscode-editor-wordHighlightBorder: #0f4a85; + --vscode-editor-wordHighlightStrongBorder: #0f4a85; + --vscode-editorOverviewRuler-wordHighlightForeground: rgba( + 160, + 160, + 160, + 0.8 + ); + --vscode-editorOverviewRuler-wordHighlightStrongForeground: rgba( + 192, + 160, + 192, + 0.8 + ); + --vscode-peekViewTitleLabel-foreground: #292929; + --vscode-peekViewTitleDescription-foreground: #292929; + --vscode-peekView-border: #0f4a85; + --vscode-peekViewResult-background: #ffffff; + --vscode-peekViewResult-lineForeground: #292929; + --vscode-peekViewResult-fileForeground: #292929; + --vscode-peekViewResult-selectionForeground: #292929; + --vscode-peekViewEditor-background: #ffffff; + --vscode-peekViewEditorGutter-background: #ffffff; + --vscode-peekViewEditor-matchHighlightBorder: #0f4a85; + --vscode-editorMarkerNavigationError-background: #0f4a85; + --vscode-editorMarkerNavigationWarning-background: #0f4a85; + --vscode-editorMarkerNavigationWarning-headerBackground: rgba( + 15, + 74, + 133, + 0.2 + ); + --vscode-editorMarkerNavigationInfo-background: #0f4a85; + --vscode-editorMarkerNavigation-background: #ffffff; + --vscode-editorSuggestWidget-background: #ffffff; + --vscode-editorSuggestWidget-border: #0f4a85; + --vscode-editorSuggestWidget-foreground: #292929; + --vscode-editorSuggestWidget-highlightForeground: #0f4a85; + --vscode-editorSuggestWidget-focusHighlightForeground: #0f4a85; + --vscode-editorSuggestWidgetStatus-foreground: rgba(41, 41, 41, 0.5); + --vscode-tab-activeBackground: #ffffff; + --vscode-tab-unfocusedActiveBackground: #ffffff; + --vscode-tab-activeForeground: #292929; + --vscode-tab-inactiveForeground: #292929; + --vscode-tab-unfocusedActiveForeground: #292929; + --vscode-tab-unfocusedInactiveForeground: #292929; + --vscode-tab-border: #0f4a85; + --vscode-tab-lastPinnedBorder: #0f4a85; + --vscode-tab-activeBorderTop: #b5200d; + --vscode-tab-unfocusedActiveBorderTop: #b5200d; + --vscode-tab-unfocusedHoverBorder: #0f4a85; + --vscode-tab-activeModifiedBorder: #0f4a85; + --vscode-tab-inactiveModifiedBorder: #0f4a85; + --vscode-tab-unfocusedActiveModifiedBorder: #0f4a85; + --vscode-tab-unfocusedInactiveModifiedBorder: #0f4a85; + --vscode-editorPane-background: #ffffff; + --vscode-editorGroup-focusedEmptyBorder: #0f4a85; + --vscode-editorGroupHeader-noTabsBackground: #ffffff; + --vscode-editorGroupHeader-border: #0f4a85; + --vscode-editorGroup-border: #0f4a85; + --vscode-editorGroup-dropBackground: rgba(15, 74, 133, 0.5); + --vscode-editorGroup-dropIntoPromptForeground: #292929; + --vscode-editorGroup-dropIntoPromptBackground: #ffffff; + --vscode-editorGroup-dropIntoPromptBorder: #0f4a85; + --vscode-sideBySideEditor-horizontalBorder: #0f4a85; + --vscode-sideBySideEditor-verticalBorder: #0f4a85; + --vscode-panel-background: #ffffff; + --vscode-panel-border: #0f4a85; + --vscode-panelTitle-activeForeground: #292929; + --vscode-panelTitle-inactiveForeground: #292929; + --vscode-panelTitle-activeBorder: #b5200d; + --vscode-panelInput-border: #0f4a85; + --vscode-panel-dropBorder: #292929; + --vscode-panelSection-dropBackground: rgba(15, 74, 133, 0.5); + --vscode-panelSectionHeader-border: #0f4a85; + --vscode-panelSection-border: #0f4a85; + --vscode-banner-background: rgba(15, 74, 133, 0.1); + --vscode-banner-iconForeground: #1a85ff; + --vscode-statusBar-foreground: #292929; + --vscode-statusBar-noFolderForeground: #292929; + --vscode-statusBar-border: #0f4a85; + --vscode-statusBar-focusBorder: #292929; + --vscode-statusBar-noFolderBorder: #0f4a85; + --vscode-statusBarItem-activeBackground: rgba(0, 0, 0, 0.18); + --vscode-statusBarItem-focusBorder: #0f4a85; + --vscode-statusBarItem-hoverBackground: rgba(0, 0, 0, 0.12); + --vscode-statusBarItem-compactHoverBackground: rgba(0, 0, 0, 0.2); + --vscode-statusBarItem-prominentForeground: #292929; + --vscode-statusBarItem-prominentBackground: rgba(0, 0, 0, 0.5); + --vscode-statusBarItem-errorBackground: #b5200d; + --vscode-statusBarItem-errorForeground: #ffffff; + --vscode-statusBarItem-warningBackground: #895503; + --vscode-statusBarItem-warningForeground: #ffffff; + --vscode-activityBar-background: #ffffff; + --vscode-activityBar-foreground: #292929; + --vscode-activityBar-inactiveForeground: #292929; + --vscode-activityBar-border: #0f4a85; + --vscode-activityBar-activeBorder: #0f4a85; + --vscode-activityBar-activeFocusBorder: #b5200d; + --vscode-activityBarBadge-background: #0f4a85; + --vscode-activityBarBadge-foreground: #ffffff; + --vscode-activityBarItem-profilesForeground: #292929; + --vscode-activityBarItem-profilesHoverForeground: #292929; + --vscode-statusBarItem-remoteBackground: #0f4a85; + --vscode-statusBarItem-remoteForeground: #ffffff; + --vscode-extensionBadge-remoteBackground: #0f4a85; + --vscode-extensionBadge-remoteForeground: #ffffff; + --vscode-sideBar-background: #ffffff; + --vscode-sideBar-border: #0f4a85; + --vscode-sideBar-dropBackground: rgba(15, 74, 133, 0.5); + --vscode-sideBarSectionHeader-border: #0f4a85; + --vscode-titleBar-activeForeground: #292929; + --vscode-titleBar-inactiveForeground: #292929; + --vscode-titleBar-activeBackground: #ffffff; + --vscode-titleBar-border: #0f4a85; + --vscode-menubar-selectionForeground: #292929; + --vscode-menubar-selectionBorder: #0f4a85; + --vscode-notificationCenter-border: #0f4a85; + --vscode-notificationToast-border: #0f4a85; + --vscode-notifications-foreground: #292929; + --vscode-notifications-background: #ffffff; + --vscode-notificationLink-foreground: #0f4a85; + --vscode-notificationCenterHeader-background: #ffffff; + --vscode-notifications-border: #ffffff; + --vscode-notificationsErrorIcon-foreground: #b5200d; + --vscode-notificationsWarningIcon-foreground: #895503; + --vscode-notificationsInfoIcon-foreground: #1a85ff; + --vscode-window-activeBorder: #0f4a85; + --vscode-window-inactiveBorder: #0f4a85; + --vscode-commandCenter-foreground: #292929; + --vscode-commandCenter-activeForeground: #292929; + --vscode-commandCenter-inactiveForeground: #292929; + --vscode-commandCenter-border: rgba(41, 41, 41, 0.6); + --vscode-commandCenter-activeBorder: #292929; + --vscode-commandCenter-inactiveBorder: rgba(41, 41, 41, 0.25); + --vscode-editorCommentsWidget-resolvedBorder: #0f4a85; + --vscode-editorCommentsWidget-unresolvedBorder: #0f4a85; + --vscode-editorCommentsWidget-rangeBackground: rgba(15, 74, 133, 0.1); + --vscode-editorCommentsWidget-rangeBorder: rgba(15, 74, 133, 0.4); + --vscode-editorCommentsWidget-rangeActiveBackground: rgba(15, 74, 133, 0.1); + --vscode-editorCommentsWidget-rangeActiveBorder: rgba(15, 74, 133, 0.2); + --vscode-editorGutter-commentRangeForeground: #000000; + --vscode-debugToolBar-background: #ffffff; + --vscode-debugIcon-startForeground: #388a34; + --vscode-editor-stackFrameHighlightBackground: rgba(255, 255, 102, 0.45); + --vscode-editor-focusedStackFrameHighlightBackground: rgba( + 206, + 231, + 206, + 0.45 + ); + --vscode-mergeEditor-change\.background: rgba(155, 185, 85, 0.2); + --vscode-mergeEditor-change\.word\.background: rgba(156, 204, 44, 0.4); + --vscode-mergeEditor-changeBase\.background: #ffcccc; + --vscode-mergeEditor-changeBase\.word\.background: #ffa3a3; + --vscode-mergeEditor-conflict\.unhandledUnfocused\.border: rgba( + 255, + 166, + 0, + 0.48 + ); + --vscode-mergeEditor-conflict\.unhandledFocused\.border: #ffa600; + --vscode-mergeEditor-conflict\.handledUnfocused\.border: rgba( + 134, + 134, + 134, + 0.29 + ); + --vscode-mergeEditor-conflict\.handledFocused\.border: rgba( + 193, + 193, + 193, + 0.8 + ); + --vscode-mergeEditor-conflict\.handled\.minimapOverViewRuler: rgba( + 173, + 172, + 168, + 0.93 + ); + --vscode-mergeEditor-conflict\.unhandled\.minimapOverViewRuler: #fcba03; + --vscode-mergeEditor-conflictingLines\.background: rgba(255, 234, 0, 0.28); + --vscode-settings-headerForeground: #292929; + --vscode-settings-modifiedItemIndicator: #66afe0; + --vscode-settings-headerBorder: #0f4a85; + --vscode-settings-sashBorder: #0f4a85; + --vscode-settings-dropdownBackground: #ffffff; + --vscode-settings-dropdownForeground: #292929; + --vscode-settings-dropdownBorder: #0f4a85; + --vscode-settings-dropdownListBorder: #0f4a85; + --vscode-settings-checkboxBackground: #ffffff; + --vscode-settings-checkboxForeground: #292929; + --vscode-settings-checkboxBorder: #0f4a85; + --vscode-settings-textInputBackground: #ffffff; + --vscode-settings-textInputForeground: #292929; + --vscode-settings-textInputBorder: #0f4a85; + --vscode-settings-numberInputBackground: #ffffff; + --vscode-settings-numberInputForeground: #292929; + --vscode-settings-numberInputBorder: #0f4a85; + --vscode-settings-focusedRowBorder: #0f4a85; + --vscode-terminal-foreground: #292929; + --vscode-terminal-selectionBackground: #0f4a85; + --vscode-terminal-inactiveSelectionBackground: rgba(15, 74, 133, 0.5); + --vscode-terminal-selectionForeground: #ffffff; + --vscode-terminalCommandDecoration-defaultBackground: rgba(0, 0, 0, 0.25); + --vscode-terminalCommandDecoration-successBackground: #007100; + --vscode-terminalCommandDecoration-errorBackground: #b5200d; + --vscode-terminalOverviewRuler-cursorForeground: rgba(160, 160, 160, 0.8); + --vscode-terminal-border: #0f4a85; + --vscode-terminal-findMatchBackground: #0f4a85; + --vscode-terminal-findMatchBorder: #0f4a85; + --vscode-terminal-findMatchHighlightBorder: #0f4a85; + --vscode-terminalOverviewRuler-findMatchForeground: #0f4a85; + --vscode-terminal-dropBackground: rgba(15, 74, 133, 0.5); + --vscode-testing-iconFailed: #b5200d; + --vscode-testing-iconErrored: #b5200d; + --vscode-testing-iconPassed: #007100; + --vscode-testing-runAction: #007100; + --vscode-testing-iconQueued: #cca700; + --vscode-testing-iconUnset: #848484; + --vscode-testing-iconSkipped: #848484; + --vscode-testing-peekBorder: #0f4a85; + --vscode-testing-message\.error\.decorationForeground: #292929; + --vscode-testing-message\.info\.decorationForeground: rgba(41, 41, 41, 0.5); + --vscode-welcomePage-tileBackground: #ffffff; + --vscode-welcomePage-tileBorder: #0f4a85; + --vscode-welcomePage-progress\.background: #ffffff; + --vscode-welcomePage-progress\.foreground: #0f4a85; + --vscode-debugExceptionWidget-border: #a31515; + --vscode-debugExceptionWidget-background: #f1dfde; + --vscode-ports-iconRunningProcessForeground: #0f4a85; + --vscode-statusBar-debuggingBackground: #b5200d; + --vscode-statusBar-debuggingForeground: #ffffff; + --vscode-statusBar-debuggingBorder: #0f4a85; + --vscode-editor-inlineValuesForeground: rgba(0, 0, 0, 0.5); + --vscode-editor-inlineValuesBackground: rgba(255, 200, 0, 0.2); + --vscode-editorGutter-modifiedBackground: #2090d3; + --vscode-editorGutter-addedBackground: #48985d; + --vscode-editorGutter-deletedBackground: #b5200d; + --vscode-minimapGutter-modifiedBackground: #2090d3; + --vscode-minimapGutter-addedBackground: #48985d; + --vscode-minimapGutter-deletedBackground: #b5200d; + --vscode-editorOverviewRuler-modifiedForeground: rgba(32, 144, 211, 0.6); + --vscode-editorOverviewRuler-addedForeground: rgba(72, 152, 93, 0.6); + --vscode-editorOverviewRuler-deletedForeground: rgba(181, 32, 13, 0.6); + --vscode-debugIcon-breakpointForeground: #e51400; + --vscode-debugIcon-breakpointDisabledForeground: #848484; + --vscode-debugIcon-breakpointUnverifiedForeground: #848484; + --vscode-debugIcon-breakpointCurrentStackframeForeground: #be8700; + --vscode-debugIcon-breakpointStackframeForeground: #89d185; + --vscode-notebook-cellBorderColor: #0f4a85; + --vscode-notebook-focusedEditorBorder: #0f4a85; + --vscode-notebookStatusSuccessIcon-foreground: #388a34; + --vscode-notebookStatusErrorIcon-foreground: #b5200d; + --vscode-notebookStatusRunningIcon-foreground: #292929; + --vscode-notebook-cellToolbarSeparator: #0f4a85; + --vscode-notebook-selectedCellBorder: #0f4a85; + --vscode-notebook-inactiveSelectedCellBorder: #0f4a85; + --vscode-notebook-focusedCellBorder: #0f4a85; + --vscode-notebook-inactiveFocusedCellBorder: #0f4a85; + --vscode-notebook-cellStatusBarItemHoverBackground: rgba(0, 0, 0, 0.08); + --vscode-notebook-cellInsertionIndicator: #0f4a85; + --vscode-notebookScrollbarSlider-background: rgba(15, 74, 133, 0.4); + --vscode-notebookScrollbarSlider-hoverBackground: rgba(15, 74, 133, 0.8); + --vscode-notebookScrollbarSlider-activeBackground: #0f4a85; + --vscode-scm-providerBorder: #0f4a85; + --vscode-searchEditor-textInputBorder: #0f4a85; + --vscode-debugTokenExpression-name: #292929; + --vscode-debugTokenExpression-value: #292929; + --vscode-debugTokenExpression-string: #a31515; + --vscode-debugTokenExpression-boolean: #0000ff; + --vscode-debugTokenExpression-number: #098658; + --vscode-debugTokenExpression-error: #e51400; + --vscode-debugView-exceptionLabelForeground: #292929; + --vscode-debugView-exceptionLabelBackground: #a31515; + --vscode-debugView-stateLabelForeground: #292929; + --vscode-debugView-stateLabelBackground: rgba(136, 136, 136, 0.27); + --vscode-debugView-valueChangedHighlight: #569cd6; + --vscode-debugConsole-infoForeground: #292929; + --vscode-debugConsole-warningForeground: #895503; + --vscode-debugConsole-errorForeground: #b5200d; + --vscode-debugConsole-sourceForeground: #292929; + --vscode-debugConsoleInputIcon-foreground: #292929; + --vscode-debugIcon-pauseForeground: #007acc; + --vscode-debugIcon-stopForeground: #a1260d; + --vscode-debugIcon-disconnectForeground: #a1260d; + --vscode-debugIcon-restartForeground: #388a34; + --vscode-debugIcon-stepOverForeground: #007acc; + --vscode-debugIcon-stepIntoForeground: #007acc; + --vscode-debugIcon-stepOutForeground: #007acc; + --vscode-debugIcon-continueForeground: #007acc; + --vscode-debugIcon-stepBackForeground: #007acc; + --vscode-extensionButton-separator: rgba(255, 255, 255, 0.4); + --vscode-extensionIcon-starForeground: #0f4a85; + --vscode-extensionIcon-verifiedForeground: #0f4a85; + --vscode-extensionIcon-preReleaseForeground: #0f4a85; + --vscode-extensionIcon-sponsorForeground: #b51e78; + --vscode-terminal-ansiBlack: #292929; + --vscode-terminal-ansiRed: #cd3131; + --vscode-terminal-ansiGreen: #00bc00; + --vscode-terminal-ansiYellow: #949800; + --vscode-terminal-ansiBlue: #0451a5; + --vscode-terminal-ansiMagenta: #bc05bc; + --vscode-terminal-ansiCyan: #ff0000; + --vscode-terminal-ansiWhite: #555555; + --vscode-terminal-ansiBrightBlack: #666666; + --vscode-terminal-ansiBrightRed: #cd3131; + --vscode-terminal-ansiBrightGreen: #00bc00; + --vscode-terminal-ansiBrightYellow: #b5ba00; + --vscode-terminal-ansiBrightBlue: #0451a5; + --vscode-terminal-ansiBrightMagenta: #bc05bc; + --vscode-terminal-ansiBrightCyan: #0598bc; + --vscode-terminal-ansiBrightWhite: #a5a5a5; + --vscode-interactive-activeCodeBorder: #0f4a85; + --vscode-interactive-inactiveCodeBorder: #0f4a85; + --vscode-gitDecoration-addedResourceForeground: #374e06; + --vscode-gitDecoration-modifiedResourceForeground: #895503; + --vscode-gitDecoration-deletedResourceForeground: #ad0707; + --vscode-gitDecoration-renamedResourceForeground: #007100; + --vscode-gitDecoration-untrackedResourceForeground: #007100; + --vscode-gitDecoration-ignoredResourceForeground: #8e8e90; + --vscode-gitDecoration-stageModifiedResourceForeground: #895503; + --vscode-gitDecoration-stageDeletedResourceForeground: #ad0707; + --vscode-gitDecoration-conflictingResourceForeground: #ad0707; + --vscode-gitDecoration-submoduleResourceForeground: #1258a7; + --vscode-testExplorer-errorDecorationBackground: #ffffff; +} + +/** + * This is copied in the same way, but from the element + */ +body { + background-color: transparent; + color: var(--vscode-editor-foreground); + font-family: var(--vscode-font-family); + font-weight: var(--vscode-font-weight); + font-size: var(--vscode-font-size); + margin: 0; + padding: 0 20px; +} + +/** + * This is used for setting the background on the Storybook preview. + */ +body { + background-color: var(--vscode-editor-background); +} From c7dbaebcecc64932d3649760f1a2ee3666a17d21 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 29 Nov 2022 17:19:14 +0100 Subject: [PATCH 49/58] Fix header color of stat item The header color of a stat item was using the badge foreground color, but badges can have a different background color than the editor. For some themes, this would result in unreadable text. By using the editor foreground color, the header should be readable in many more themes. --- extensions/ql-vscode/src/view/variant-analysis/StatItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/view/variant-analysis/StatItem.tsx b/extensions/ql-vscode/src/view/variant-analysis/StatItem.tsx index d5e188e23..35815efac 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/StatItem.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/StatItem.tsx @@ -12,7 +12,7 @@ const Container = styled.div` `; const Header = styled.div` - color: var(--vscode-badge-foreground); + color: var(--vscode-editor-foreground); font-size: 0.85em; font-weight: 800; text-transform: uppercase; From 4771d9b834ac584f91318f65b3207146c05679f6 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 29 Nov 2022 16:33:03 +0000 Subject: [PATCH 50/58] Add tests of a pending and failed variant analysis --- .../view/variant-analysis/VariantAnalysis.tsx | 4 +- .../__tests__/VariantAnalysis.spec.tsx | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx index 02b4f8957..9ec2a132b 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx @@ -17,7 +17,7 @@ import { RepositoriesFilterSortState, } from "../../pure/variant-analysis-filter-sort"; -type Props = { +export type VariantAnalysisProps = { variantAnalysis?: VariantAnalysisDomainModel; repoStates?: VariantAnalysisScannedRepositoryState[]; repoResults?: VariantAnalysisScannedRepositoryResult[]; @@ -51,7 +51,7 @@ export function VariantAnalysis({ variantAnalysis: initialVariantAnalysis, repoStates: initialRepoStates = [], repoResults: initialRepoResults = [], -}: Props): JSX.Element { +}: VariantAnalysisProps): JSX.Element { const [variantAnalysis, setVariantAnalysis] = useState< VariantAnalysisDomainModel | undefined >(initialVariantAnalysis); diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx new file mode 100644 index 000000000..a073ef331 --- /dev/null +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx @@ -0,0 +1,49 @@ +import * as React from "react"; +import { render as reactRender, screen } from "@testing-library/react"; +import { + VariantAnalysisFailureReason, + VariantAnalysisStatus, +} from "../../../remote-queries/shared/variant-analysis"; +import { VariantAnalysis, VariantAnalysisProps } from "../VariantAnalysis"; +import { createMockVariantAnalysis } from "../../../vscode-tests/factories/remote-queries/shared/variant-analysis"; + +describe(VariantAnalysis.name, () => { + const render = (props: Partial = {}) => + reactRender( + , + ); + + it("renders a pending analysis", () => { + const variantAnalysis = createMockVariantAnalysis({ + status: VariantAnalysisStatus.InProgress, + }); + variantAnalysis.actionsWorkflowRunId = undefined; + render({ variantAnalysis }); + + expect( + screen.getByText("We are getting everything ready"), + ).toBeInTheDocument(); + }); + + it("renders an analysis where there were no repos to analyse", () => { + const variantAnalysis = createMockVariantAnalysis({ + status: VariantAnalysisStatus.Failed, + }); + variantAnalysis.failureReason = VariantAnalysisFailureReason.NoReposQueried; + variantAnalysis.actionsWorkflowRunId = undefined; + render({ variantAnalysis }); + + expect( + screen.queryByText("We are getting everything ready"), + ).not.toBeInTheDocument(); + + expect( + screen.getByText( + "No repositories available after processing. No repositories were analyzed.", + ), + ).toBeInTheDocument(); + }); +}); From 2637d6d00c1060fc80d818bd02b29b55cf2828ed Mon Sep 17 00:00:00 2001 From: Alexander Eyers-Taylor Date: Tue, 29 Nov 2022 17:24:54 +0000 Subject: [PATCH 51/58] Added changelog entry for updated vscode version. --- extensions/ql-vscode/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 8d4ca1461..8f600cb7a 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,6 +2,8 @@ ## [UNRELEASED] +- Required version of VS Code increased to 1.67.0. + ## 1.7.6 - 21 November 2022 - Warn users when their VS Code version is too old to support all features in the vscode-codeql extension. [#1674](https://github.com/github/vscode-codeql/pull/1674) From 8f0e6154ad9f35e7cb63b89a410a3a661b275834 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 24 Nov 2022 15:16:28 +0100 Subject: [PATCH 52/58] Remove variant analysis monitor return value The monitor return value was only used in tests, but we can also assert the correct behavior using the calls it makes, rather than using the result of the monitor. --- .../shared/variant-analysis-monitor-result.ts | 9 --- .../variant-analysis-monitor.ts | 7 +- .../variant-analysis-monitor.test.ts | 70 ++++++------------- 3 files changed, 23 insertions(+), 63 deletions(-) delete mode 100644 extensions/ql-vscode/src/remote-queries/shared/variant-analysis-monitor-result.ts diff --git a/extensions/ql-vscode/src/remote-queries/shared/variant-analysis-monitor-result.ts b/extensions/ql-vscode/src/remote-queries/shared/variant-analysis-monitor-result.ts deleted file mode 100644 index 5f9f1eee8..000000000 --- a/extensions/ql-vscode/src/remote-queries/shared/variant-analysis-monitor-result.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { VariantAnalysis } from "./variant-analysis"; - -export type VariantAnalysisMonitorStatus = "Completed" | "Canceled"; - -export interface VariantAnalysisMonitorResult { - status: VariantAnalysisMonitorStatus; - scannedReposDownloaded?: number[]; - variantAnalysis?: VariantAnalysis; -} diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-monitor.ts b/extensions/ql-vscode/src/remote-queries/variant-analysis-monitor.ts index 8f05cd8a7..8aeab0b14 100644 --- a/extensions/ql-vscode/src/remote-queries/variant-analysis-monitor.ts +++ b/extensions/ql-vscode/src/remote-queries/variant-analysis-monitor.ts @@ -13,7 +13,6 @@ import { VariantAnalysis, VariantAnalysisScannedRepository, } from "./shared/variant-analysis"; -import { VariantAnalysisMonitorResult } from "./shared/variant-analysis-monitor-result"; import { processUpdatedVariantAnalysis } from "./variant-analysis-processor"; import { DisposableObject } from "../pure/disposable-object"; import { sleep } from "../pure/time"; @@ -36,7 +35,7 @@ export class VariantAnalysisMonitor extends DisposableObject { public async monitorVariantAnalysis( variantAnalysis: VariantAnalysis, cancellationToken: CancellationToken, - ): Promise { + ): Promise { const credentials = await Credentials.initialize(this.extensionContext); if (!credentials) { throw Error("Error authenticating with GitHub"); @@ -49,7 +48,7 @@ export class VariantAnalysisMonitor extends DisposableObject { await sleep(VariantAnalysisMonitor.sleepTime); if (cancellationToken && cancellationToken.isCancellationRequested) { - return { status: "Canceled" }; + return; } const variantAnalysisSummary = await ghApiClient.getVariantAnalysis( @@ -77,8 +76,6 @@ export class VariantAnalysisMonitor extends DisposableObject { attemptCount++; } - - return { status: "Completed", scannedReposDownloaded, variantAnalysis }; } private scheduleForDownload( diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts index 9310fc576..c423a6d20 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-monitor.test.ts @@ -42,6 +42,8 @@ describe("Variant Analysis Monitor", () => { typeof variantAnalysisManager.autoDownloadVariantAnalysisResult >; + const onVariantAnalysisChangeSpy = jest.fn(); + beforeEach(async () => { jest .spyOn(config, "isVariantAnalysisLiveResultsEnabled") @@ -57,6 +59,7 @@ describe("Variant Analysis Monitor", () => { )! .activate(); variantAnalysisMonitor = new VariantAnalysisMonitor(extension.ctx); + variantAnalysisMonitor.onVariantAnalysisChange(onVariantAnalysisChangeSpy); variantAnalysisManager = extension.variantAnalysisManager; mockGetDownloadResult = jest @@ -103,12 +106,12 @@ describe("Variant Analysis Monitor", () => { it("should return early if variant analysis is cancelled", async () => { cancellationTokenSource.cancel(); - const result = await variantAnalysisMonitor.monitorVariantAnalysis( + await variantAnalysisMonitor.monitorVariantAnalysis( variantAnalysis, cancellationTokenSource.token, ); - expect(result).toEqual({ status: "Canceled" }); + expect(onVariantAnalysisChangeSpy).not.toHaveBeenCalled(); }); describe("when the variant analysis fails", () => { @@ -119,34 +122,22 @@ describe("Variant Analysis Monitor", () => { mockGetVariantAnalysis.mockResolvedValue(mockFailedApiResponse); }); - it("should mark as failed locally and stop monitoring", async () => { - const result = await variantAnalysisMonitor.monitorVariantAnalysis( + it("should mark as failed and stop monitoring", async () => { + await variantAnalysisMonitor.monitorVariantAnalysis( variantAnalysis, cancellationTokenSource.token, ); expect(mockGetVariantAnalysis).toHaveBeenCalledTimes(1); - expect(result.status).toEqual("Completed"); - expect(result.variantAnalysis?.status).toBe( - VariantAnalysisStatus.Failed, - ); - expect(result.variantAnalysis?.failureReason).toBe( - processFailureReason( - mockFailedApiResponse.failure_reason as VariantAnalysisFailureReason, - ), - ); - }); - it("should emit `onVariantAnalysisChange`", async () => { - const spy = jest.fn(); - variantAnalysisMonitor.onVariantAnalysisChange(spy); - - const result = await variantAnalysisMonitor.monitorVariantAnalysis( - variantAnalysis, - cancellationTokenSource.token, + expect(onVariantAnalysisChangeSpy).toHaveBeenCalledWith( + expect.objectContaining({ + status: VariantAnalysisStatus.Failed, + failureReason: processFailureReason( + mockFailedApiResponse.failure_reason as VariantAnalysisFailureReason, + ), + }), ); - - expect(spy).toBeCalledWith(result.variantAnalysis); }); }); @@ -173,18 +164,6 @@ describe("Variant Analysis Monitor", () => { ); }); - it("should succeed and return a list of scanned repo ids", async () => { - const result = await variantAnalysisMonitor.monitorVariantAnalysis( - variantAnalysis, - cancellationTokenSource.token, - ); - - expect(result.status).toBe("Completed"); - expect(result.scannedReposDownloaded).toEqual( - succeededRepos.map((r) => r.repository.id), - ); - }); - it("should trigger a download extension command for each repo", async () => { const succeededRepos = scannedRepos.filter( (r) => r.analysis_status === "succeeded", @@ -238,14 +217,17 @@ describe("Variant Analysis Monitor", () => { mockGetVariantAnalysis.mockResolvedValue(mockApiResponse); }); - it("should succeed and return an empty list of scanned repo ids", async () => { - const result = await variantAnalysisMonitor.monitorVariantAnalysis( + it("should succeed and not download any repos via a command", async () => { + const commandSpy = jest + .spyOn(commands, "executeCommand") + .mockResolvedValue(undefined); + + await variantAnalysisMonitor.monitorVariantAnalysis( variantAnalysis, cancellationTokenSource.token, ); - expect(result.status).toBe("Completed"); - expect(result.scannedReposDownloaded).toEqual([]); + expect(commandSpy).not.toHaveBeenCalled(); }); it("should not try to download any repos", async () => { @@ -265,16 +247,6 @@ describe("Variant Analysis Monitor", () => { mockGetVariantAnalysis.mockResolvedValue(mockApiResponse); }); - it("should succeed and return an empty list of scanned repo ids", async () => { - const result = await variantAnalysisMonitor.monitorVariantAnalysis( - variantAnalysis, - cancellationTokenSource.token, - ); - - expect(result.status).toBe("Completed"); - expect(result.scannedReposDownloaded).toEqual([]); - }); - it("should not try to download any repos", async () => { await variantAnalysisMonitor.monitorVariantAnalysis( variantAnalysis, From 74e047cbd883c550a9c253a0cd19e1774c962d6c Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Wed, 30 Nov 2022 09:45:29 +0000 Subject: [PATCH 53/58] Move logging code to /common/logging and split into multiple files (#1800) --- .../src/archive-filesystem-provider.ts | 2 +- extensions/ql-vscode/src/cli-version.ts | 2 +- extensions/ql-vscode/src/cli.ts | 2 +- extensions/ql-vscode/src/commandRunner.ts | 2 +- extensions/ql-vscode/src/common/index.ts | 1 + .../ql-vscode/src/common/logging/index.ts | 3 ++ .../ql-vscode/src/common/logging/logger.ts | 24 ++++++++++ .../src/common/logging/vscode/loggers.ts | 15 +++++++ .../logging/vscode/output-channel-logger.ts} | 44 ++----------------- .../ql-vscode/src/compare/compare-view.ts | 2 +- extensions/ql-vscode/src/config.ts | 2 +- .../ql-vscode/src/contextual/queryResolver.ts | 2 +- extensions/ql-vscode/src/databaseFetcher.ts | 2 +- extensions/ql-vscode/src/databases-ui.ts | 2 +- extensions/ql-vscode/src/databases.ts | 2 +- .../ql-vscode/src/databases/db-module.ts | 2 +- extensions/ql-vscode/src/discovery.ts | 2 +- extensions/ql-vscode/src/distribution.ts | 2 +- extensions/ql-vscode/src/extension.ts | 2 +- extensions/ql-vscode/src/helpers.ts | 2 +- extensions/ql-vscode/src/ide-server.ts | 2 +- extensions/ql-vscode/src/interface-utils.ts | 2 +- extensions/ql-vscode/src/interface.ts | 2 +- extensions/ql-vscode/src/json-rpc-server.ts | 2 +- .../legacy-query-server/queryserver-client.ts | 2 +- .../src/legacy-query-server/run-queries.ts | 2 +- .../src/legacy-query-server/upgrades.ts | 2 +- .../src/log-insights/log-scanner-service.ts | 2 +- .../log-insights/summary-language-support.ts | 2 +- extensions/ql-vscode/src/packaging.ts | 2 +- .../ql-vscode/src/query-history-scrubber.ts | 2 +- extensions/ql-vscode/src/query-history.ts | 2 +- .../src/query-server/queryserver-client.ts | 2 +- .../ql-vscode/src/query-server/run-queries.ts | 2 +- .../analyses-results-manager.ts | 2 +- .../src/remote-queries/bqrs-processing.ts | 2 +- .../src/remote-queries/export-results.ts | 2 +- .../gh-api/gh-actions-api-client.ts | 2 +- .../remote-queries/remote-queries-manager.ts | 2 +- .../remote-queries/remote-queries-monitor.ts | 2 +- .../src/remote-queries/remote-queries-view.ts | 2 +- .../remote-queries/repository-selection.ts | 2 +- .../src/remote-queries/run-remote-query.ts | 2 +- .../variant-analysis-results-manager.ts | 2 +- .../remote-queries/variant-analysis-view.ts | 2 +- .../ql-vscode/src/run-queries-shared.ts | 3 +- extensions/ql-vscode/src/telemetry.ts | 2 +- extensions/ql-vscode/src/test-adapter.ts | 2 +- extensions/ql-vscode/src/test-ui.ts | 2 +- .../cli-integration/legacy-query.test.ts | 2 +- .../cli-integration/new-query.test.ts | 2 +- .../remote-queries-manager.test.ts | 2 +- .../variant-analysis-manager.test.ts | 2 +- .../variant-analysis-results-manager.test.ts | 2 +- .../minimal-workspace/databases.test.ts | 2 +- .../no-workspace/distribution.test.ts | 2 +- .../no-workspace/query-history.test.ts | 2 +- .../logging/output-channel-logger.test.ts} | 2 +- 58 files changed, 100 insertions(+), 94 deletions(-) create mode 100644 extensions/ql-vscode/src/common/index.ts create mode 100644 extensions/ql-vscode/src/common/logging/index.ts create mode 100644 extensions/ql-vscode/src/common/logging/logger.ts create mode 100644 extensions/ql-vscode/src/common/logging/vscode/loggers.ts rename extensions/ql-vscode/src/{logging.ts => common/logging/vscode/output-channel-logger.ts} (70%) rename extensions/ql-vscode/test/{pure-tests/logging.test.ts => common/logging/output-channel-logger.test.ts} (97%) diff --git a/extensions/ql-vscode/src/archive-filesystem-provider.ts b/extensions/ql-vscode/src/archive-filesystem-provider.ts index 90137e2cd..8681d681a 100644 --- a/extensions/ql-vscode/src/archive-filesystem-provider.ts +++ b/extensions/ql-vscode/src/archive-filesystem-provider.ts @@ -1,7 +1,7 @@ import * as fs from "fs-extra"; import * as unzipper from "unzipper"; import * as vscode from "vscode"; -import { logger } from "./logging"; +import { logger } from "./common"; // All path operations in this file must be on paths *within* the zip // archive. diff --git a/extensions/ql-vscode/src/cli-version.ts b/extensions/ql-vscode/src/cli-version.ts index 6ada4ea71..d7a971235 100644 --- a/extensions/ql-vscode/src/cli-version.ts +++ b/extensions/ql-vscode/src/cli-version.ts @@ -1,6 +1,6 @@ import * as semver from "semver"; import { runCodeQlCliCommand } from "./cli"; -import { Logger } from "./logging"; +import { Logger } from "./common"; import { getErrorMessage } from "./pure/helpers-pure"; /** diff --git a/extensions/ql-vscode/src/cli.ts b/extensions/ql-vscode/src/cli.ts index d51445c58..ae4f56dac 100644 --- a/extensions/ql-vscode/src/cli.ts +++ b/extensions/ql-vscode/src/cli.ts @@ -22,7 +22,7 @@ import { getErrorStack, } from "./pure/helpers-pure"; import { QueryMetadata, SortDirection } from "./pure/interface-types"; -import { Logger, ProgressReporter } from "./logging"; +import { Logger, ProgressReporter } from "./common"; import { CompilationMessage } from "./pure/legacy-messages"; import { sarifParser } from "./sarif-parser"; import { dbSchemeToLanguage, walkDirectory } from "./helpers"; diff --git a/extensions/ql-vscode/src/commandRunner.ts b/extensions/ql-vscode/src/commandRunner.ts index 5820b6adb..a0881d4b4 100644 --- a/extensions/ql-vscode/src/commandRunner.ts +++ b/extensions/ql-vscode/src/commandRunner.ts @@ -7,7 +7,7 @@ import { ProgressLocation, } from "vscode"; import { showAndLogErrorMessage, showAndLogWarningMessage } from "./helpers"; -import { logger } from "./logging"; +import { logger } from "./common"; import { getErrorMessage, getErrorStack } from "./pure/helpers-pure"; import { telemetryListener } from "./telemetry"; diff --git a/extensions/ql-vscode/src/common/index.ts b/extensions/ql-vscode/src/common/index.ts new file mode 100644 index 000000000..2c1bfd0d5 --- /dev/null +++ b/extensions/ql-vscode/src/common/index.ts @@ -0,0 +1 @@ +export * from "./logging"; diff --git a/extensions/ql-vscode/src/common/logging/index.ts b/extensions/ql-vscode/src/common/logging/index.ts new file mode 100644 index 000000000..35ed72fd7 --- /dev/null +++ b/extensions/ql-vscode/src/common/logging/index.ts @@ -0,0 +1,3 @@ +export * from "./logger"; +export * from "./vscode/loggers"; +export * from "./vscode/output-channel-logger"; diff --git a/extensions/ql-vscode/src/common/logging/logger.ts b/extensions/ql-vscode/src/common/logging/logger.ts new file mode 100644 index 000000000..b66cb1054 --- /dev/null +++ b/extensions/ql-vscode/src/common/logging/logger.ts @@ -0,0 +1,24 @@ +export interface LogOptions { + /** If false, don't output a trailing newline for the log entry. Default true. */ + trailingNewline?: boolean; + + /** If specified, add this log entry to the log file at the specified location. */ + additionalLogLocation?: string; +} + +export interface Logger { + /** Writes the given log message, optionally followed by a newline. */ + log(message: string, options?: LogOptions): Promise; + /** + * Reveal this channel in the UI. + * + * @param preserveFocus When `true` the channel will not take focus. + */ + show(preserveFocus?: boolean): void; + + /** + * Remove the log at the specified location + * @param location log to remove + */ + removeAdditionalLogLocation(location: string | undefined): void; +} diff --git a/extensions/ql-vscode/src/common/logging/vscode/loggers.ts b/extensions/ql-vscode/src/common/logging/vscode/loggers.ts new file mode 100644 index 000000000..abebd2fa5 --- /dev/null +++ b/extensions/ql-vscode/src/common/logging/vscode/loggers.ts @@ -0,0 +1,15 @@ +import { OutputChannelLogger } from "./output-channel-logger"; + +/** The global logger for the extension. */ +export const logger = new OutputChannelLogger("CodeQL Extension Log"); + +/** The logger for messages from the query server. */ +export const queryServerLogger = new OutputChannelLogger("CodeQL Query Server"); + +/** The logger for messages from the language server. */ +export const ideServerLogger = new OutputChannelLogger( + "CodeQL Language Server", +); + +/** The logger for messages from tests. */ +export const testLogger = new OutputChannelLogger("CodeQL Tests"); diff --git a/extensions/ql-vscode/src/logging.ts b/extensions/ql-vscode/src/common/logging/vscode/output-channel-logger.ts similarity index 70% rename from extensions/ql-vscode/src/logging.ts rename to extensions/ql-vscode/src/common/logging/vscode/output-channel-logger.ts index e137e433e..c8b37bfbe 100644 --- a/extensions/ql-vscode/src/logging.ts +++ b/extensions/ql-vscode/src/common/logging/vscode/output-channel-logger.ts @@ -1,34 +1,8 @@ import { window as Window, OutputChannel, Progress } from "vscode"; -import { DisposableObject } from "./pure/disposable-object"; import * as fs from "fs-extra"; import * as path from "path"; - -interface LogOptions { - /** If false, don't output a trailing newline for the log entry. Default true. */ - trailingNewline?: boolean; - - /** If specified, add this log entry to the log file at the specified location. */ - additionalLogLocation?: string; -} - -export interface Logger { - /** Writes the given log message, optionally followed by a newline. */ - log(message: string, options?: LogOptions): Promise; - /** - * Reveal this channel in the UI. - * - * @param preserveFocus When `true` the channel will not take focus. - */ - show(preserveFocus?: boolean): void; - - /** - * Remove the log at the specified location - * @param location log to remove - */ - removeAdditionalLogLocation(location: string | undefined): void; -} - -export type ProgressReporter = Progress<{ message: string }>; +import { Logger, LogOptions } from "../logger"; +import { DisposableObject } from "../../../pure/disposable-object"; /** A logger that writes messages to an output channel in the Output tab. */ export class OutputChannelLogger extends DisposableObject implements Logger { @@ -128,16 +102,4 @@ class AdditionalLogLocation { } } -/** The global logger for the extension. */ -export const logger = new OutputChannelLogger("CodeQL Extension Log"); - -/** The logger for messages from the query server. */ -export const queryServerLogger = new OutputChannelLogger("CodeQL Query Server"); - -/** The logger for messages from the language server. */ -export const ideServerLogger = new OutputChannelLogger( - "CodeQL Language Server", -); - -/** The logger for messages from tests. */ -export const testLogger = new OutputChannelLogger("CodeQL Tests"); +export type ProgressReporter = Progress<{ message: string }>; diff --git a/extensions/ql-vscode/src/compare/compare-view.ts b/extensions/ql-vscode/src/compare/compare-view.ts index 8da560277..108d32b06 100644 --- a/extensions/ql-vscode/src/compare/compare-view.ts +++ b/extensions/ql-vscode/src/compare/compare-view.ts @@ -5,7 +5,7 @@ import { ToCompareViewMessage, QueryCompareResult, } from "../pure/interface-types"; -import { Logger } from "../logging"; +import { Logger } from "../common"; import { CodeQLCliServer } from "../cli"; import { DatabaseManager } from "../databases"; import { jumpToLocation } from "../interface-utils"; diff --git a/extensions/ql-vscode/src/config.ts b/extensions/ql-vscode/src/config.ts index d7aa6f786..5ac442afe 100644 --- a/extensions/ql-vscode/src/config.ts +++ b/extensions/ql-vscode/src/config.ts @@ -7,7 +7,7 @@ import { ConfigurationTarget, } from "vscode"; import { DistributionManager } from "./distribution"; -import { logger } from "./logging"; +import { logger } from "./common"; import { ONE_DAY_IN_MS } from "./pure/time"; export const ALL_SETTINGS: Setting[] = []; diff --git a/extensions/ql-vscode/src/contextual/queryResolver.ts b/extensions/ql-vscode/src/contextual/queryResolver.ts index de55bdcab..ecb2f30d7 100644 --- a/extensions/ql-vscode/src/contextual/queryResolver.ts +++ b/extensions/ql-vscode/src/contextual/queryResolver.ts @@ -8,7 +8,7 @@ import { KeyType, kindOfKeyType, nameOfKeyType, tagOfKeyType } from "./keyType"; import { CodeQLCliServer } from "../cli"; import { DatabaseItem } from "../databases"; import { QlPacksForLanguage } from "../helpers"; -import { logger } from "../logging"; +import { logger } from "../common"; import { createInitialQueryInfo } from "../run-queries-shared"; import { CancellationToken, Uri } from "vscode"; import { ProgressCallback } from "../commandRunner"; diff --git a/extensions/ql-vscode/src/databaseFetcher.ts b/extensions/ql-vscode/src/databaseFetcher.ts index c10ec6251..40d5a9546 100644 --- a/extensions/ql-vscode/src/databaseFetcher.ts +++ b/extensions/ql-vscode/src/databaseFetcher.ts @@ -11,7 +11,7 @@ import { retry } from "@octokit/plugin-retry"; import { DatabaseManager, DatabaseItem } from "./databases"; import { showAndLogInformationMessage } from "./helpers"; import { reportStreamProgress, ProgressCallback } from "./commandRunner"; -import { logger } from "./logging"; +import { logger } from "./common"; import { tmpDir } from "./helpers"; import { Credentials } from "./authentication"; import { REPO_REGEX, getErrorMessage } from "./pure/helpers-pure"; diff --git a/extensions/ql-vscode/src/databases-ui.ts b/extensions/ql-vscode/src/databases-ui.ts index 885a5f9a7..902ba0532 100644 --- a/extensions/ql-vscode/src/databases-ui.ts +++ b/extensions/ql-vscode/src/databases-ui.ts @@ -27,7 +27,7 @@ import { isLikelyDbLanguageFolder, showAndLogErrorMessage, } from "./helpers"; -import { logger } from "./logging"; +import { logger } from "./common"; import { importArchiveDatabase, promptImportGithubDatabase, diff --git a/extensions/ql-vscode/src/databases.ts b/extensions/ql-vscode/src/databases.ts index ef968f927..f27b43f4d 100644 --- a/extensions/ql-vscode/src/databases.ts +++ b/extensions/ql-vscode/src/databases.ts @@ -18,7 +18,7 @@ import { encodeSourceArchiveUri, } from "./archive-filesystem-provider"; import { DisposableObject } from "./pure/disposable-object"; -import { Logger, logger } from "./logging"; +import { Logger, logger } from "./common"; import { getErrorMessage } from "./pure/helpers-pure"; import { QueryRunner } from "./queryRunner"; diff --git a/extensions/ql-vscode/src/databases/db-module.ts b/extensions/ql-vscode/src/databases/db-module.ts index 9f4d4d463..d7e08bfa4 100644 --- a/extensions/ql-vscode/src/databases/db-module.ts +++ b/extensions/ql-vscode/src/databases/db-module.ts @@ -1,6 +1,6 @@ import { App, AppMode } from "../common/app"; import { isCanary, isNewQueryRunExperienceEnabled } from "../config"; -import { logger } from "../logging"; +import { logger } from "../common"; import { DisposableObject } from "../pure/disposable-object"; import { DbConfigStore } from "./config/db-config-store"; import { DbManager } from "./db-manager"; diff --git a/extensions/ql-vscode/src/discovery.ts b/extensions/ql-vscode/src/discovery.ts index 69fdffaa8..95baaa2dc 100644 --- a/extensions/ql-vscode/src/discovery.ts +++ b/extensions/ql-vscode/src/discovery.ts @@ -1,5 +1,5 @@ import { DisposableObject } from "./pure/disposable-object"; -import { logger } from "./logging"; +import { logger } from "./common"; /** * Base class for "discovery" operations, which scan the file system to find specific kinds of diff --git a/extensions/ql-vscode/src/distribution.ts b/extensions/ql-vscode/src/distribution.ts index 46c33d19f..2e9563296 100644 --- a/extensions/ql-vscode/src/distribution.ts +++ b/extensions/ql-vscode/src/distribution.ts @@ -12,7 +12,7 @@ import { showAndLogErrorMessage, showAndLogWarningMessage, } from "./helpers"; -import { logger } from "./logging"; +import { logger } from "./common"; import { getCodeQlCliVersion } from "./cli-version"; import { ProgressCallback, reportStreamProgress } from "./commandRunner"; import { diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 86147e11c..0a77389fd 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -79,7 +79,7 @@ import { logger, ProgressReporter, queryServerLogger, -} from "./logging"; +} from "./common"; import { QueryHistoryManager } from "./query-history"; import { CompletedLocalQueryInfo, LocalQueryInfo } from "./query-results"; import * as legacyQueryServer from "./legacy-query-server/queryserver-client"; diff --git a/extensions/ql-vscode/src/helpers.ts b/extensions/ql-vscode/src/helpers.ts index 0dd592eb9..28a82d481 100644 --- a/extensions/ql-vscode/src/helpers.ts +++ b/extensions/ql-vscode/src/helpers.ts @@ -12,7 +12,7 @@ import { } from "vscode"; import { CodeQLCliServer, QlpacksInfo } from "./cli"; import { UserCancellationException } from "./commandRunner"; -import { logger } from "./logging"; +import { logger } from "./common"; import { QueryMetadata } from "./pure/interface-types"; // Shared temporary folder for the extension. diff --git a/extensions/ql-vscode/src/ide-server.ts b/extensions/ql-vscode/src/ide-server.ts index c210ef7a6..c38360cf9 100644 --- a/extensions/ql-vscode/src/ide-server.ts +++ b/extensions/ql-vscode/src/ide-server.ts @@ -2,7 +2,7 @@ import { ProgressLocation, window } from "vscode"; import { StreamInfo } from "vscode-languageclient/node"; import * as cli from "./cli"; import { QueryServerConfig } from "./config"; -import { ideServerLogger } from "./logging"; +import { ideServerLogger } from "./common"; /** * Managing the language server for CodeQL. diff --git a/extensions/ql-vscode/src/interface-utils.ts b/extensions/ql-vscode/src/interface-utils.ts index 5203d88f9..f79623fc5 100644 --- a/extensions/ql-vscode/src/interface-utils.ts +++ b/extensions/ql-vscode/src/interface-utils.ts @@ -17,7 +17,7 @@ import { import { tryGetResolvableLocation, isLineColumnLoc } from "./pure/bqrs-utils"; import { DatabaseItem, DatabaseManager } from "./databases"; import { ViewSourceFileMsg } from "./pure/interface-types"; -import { Logger } from "./logging"; +import { Logger } from "./common"; import { LineColumnLocation, WholeFileLocation, diff --git a/extensions/ql-vscode/src/interface.ts b/extensions/ql-vscode/src/interface.ts index 56e60e0ab..9a1679e51 100644 --- a/extensions/ql-vscode/src/interface.ts +++ b/extensions/ql-vscode/src/interface.ts @@ -34,7 +34,7 @@ import { RawResultsSortState, NavigationDirection, } from "./pure/interface-types"; -import { Logger } from "./logging"; +import { Logger } from "./common"; import { commandRunner } from "./commandRunner"; import { CompletedQueryInfo, diff --git a/extensions/ql-vscode/src/json-rpc-server.ts b/extensions/ql-vscode/src/json-rpc-server.ts index f674ce73d..d6f7db2c8 100644 --- a/extensions/ql-vscode/src/json-rpc-server.ts +++ b/extensions/ql-vscode/src/json-rpc-server.ts @@ -1,4 +1,4 @@ -import { Logger } from "./logging"; +import { Logger } from "./common"; import * as cp from "child_process"; import { Disposable } from "vscode"; import { MessageConnection } from "vscode-jsonrpc"; diff --git a/extensions/ql-vscode/src/legacy-query-server/queryserver-client.ts b/extensions/ql-vscode/src/legacy-query-server/queryserver-client.ts index 73348b3bc..94eb9a512 100644 --- a/extensions/ql-vscode/src/legacy-query-server/queryserver-client.ts +++ b/extensions/ql-vscode/src/legacy-query-server/queryserver-client.ts @@ -6,7 +6,7 @@ import { CancellationToken, commands } from "vscode"; import { createMessageConnection, RequestType } from "vscode-jsonrpc/node"; import * as cli from "../cli"; import { QueryServerConfig } from "../config"; -import { Logger, ProgressReporter } from "../logging"; +import { Logger, ProgressReporter } from "../common"; import { completeQuery, EvaluationResult, diff --git a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts index fe10bad44..96edbf308 100644 --- a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts +++ b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts @@ -16,7 +16,7 @@ import { } from "../helpers"; import { ProgressCallback } from "../commandRunner"; import { QueryMetadata } from "../pure/interface-types"; -import { logger } from "../logging"; +import { logger } from "../common"; import * as messages from "../pure/legacy-messages"; import { InitialQueryInfo, LocalQueryInfo } from "../query-results"; import * as qsClient from "./queryserver-client"; diff --git a/extensions/ql-vscode/src/legacy-query-server/upgrades.ts b/extensions/ql-vscode/src/legacy-query-server/upgrades.ts index dcf0a9d79..f38aaeb77 100644 --- a/extensions/ql-vscode/src/legacy-query-server/upgrades.ts +++ b/extensions/ql-vscode/src/legacy-query-server/upgrades.ts @@ -5,7 +5,7 @@ import { tmpDir, } from "../helpers"; import { ProgressCallback, UserCancellationException } from "../commandRunner"; -import { logger } from "../logging"; +import { logger } from "../common"; import * as messages from "../pure/legacy-messages"; import * as qsClient from "./queryserver-client"; import * as tmp from "tmp-promise"; diff --git a/extensions/ql-vscode/src/log-insights/log-scanner-service.ts b/extensions/ql-vscode/src/log-insights/log-scanner-service.ts index 3b6f53acd..d82492ccb 100644 --- a/extensions/ql-vscode/src/log-insights/log-scanner-service.ts +++ b/extensions/ql-vscode/src/log-insights/log-scanner-service.ts @@ -8,7 +8,7 @@ import { } from "./log-scanner"; import { PipelineInfo, SummarySymbols } from "./summary-parser"; import * as fs from "fs-extra"; -import { logger } from "../logging"; +import { logger } from "../common"; /** * Compute the key used to find a predicate in the summary symbols. diff --git a/extensions/ql-vscode/src/log-insights/summary-language-support.ts b/extensions/ql-vscode/src/log-insights/summary-language-support.ts index 0712e4e0c..8d4d17f28 100644 --- a/extensions/ql-vscode/src/log-insights/summary-language-support.ts +++ b/extensions/ql-vscode/src/log-insights/summary-language-support.ts @@ -14,7 +14,7 @@ import { } from "vscode"; import { DisposableObject } from "../pure/disposable-object"; import { commandRunner } from "../commandRunner"; -import { logger } from "../logging"; +import { logger } from "../common"; import { getErrorMessage } from "../pure/helpers-pure"; /** A `Position` within a specified file on disk. */ diff --git a/extensions/ql-vscode/src/packaging.ts b/extensions/ql-vscode/src/packaging.ts index eaa44f7de..895d9984d 100644 --- a/extensions/ql-vscode/src/packaging.ts +++ b/extensions/ql-vscode/src/packaging.ts @@ -6,7 +6,7 @@ import { } from "./helpers"; import { QuickPickItem, window } from "vscode"; import { ProgressCallback, UserCancellationException } from "./commandRunner"; -import { logger } from "./logging"; +import { logger } from "./common"; const QUERY_PACKS = [ "codeql/cpp-queries", diff --git a/extensions/ql-vscode/src/query-history-scrubber.ts b/extensions/ql-vscode/src/query-history-scrubber.ts index 4e7e86ca6..f89659082 100644 --- a/extensions/ql-vscode/src/query-history-scrubber.ts +++ b/extensions/ql-vscode/src/query-history-scrubber.ts @@ -2,7 +2,7 @@ import * as fs from "fs-extra"; import * as os from "os"; import * as path from "path"; import { Disposable, ExtensionContext } from "vscode"; -import { logger } from "./logging"; +import { logger } from "./common"; import { QueryHistoryManager } from "./query-history"; const LAST_SCRUB_TIME_KEY = "lastScrubTime"; diff --git a/extensions/ql-vscode/src/query-history.ts b/extensions/ql-vscode/src/query-history.ts index adc68d9a1..3a63cade5 100644 --- a/extensions/ql-vscode/src/query-history.ts +++ b/extensions/ql-vscode/src/query-history.ts @@ -24,7 +24,7 @@ import { showAndLogWarningMessage, showBinaryChoiceDialog, } from "./helpers"; -import { logger } from "./logging"; +import { logger } from "./common"; import { URLSearchParams } from "url"; import { DisposableObject } from "./pure/disposable-object"; import { commandRunner } from "./commandRunner"; diff --git a/extensions/ql-vscode/src/query-server/queryserver-client.ts b/extensions/ql-vscode/src/query-server/queryserver-client.ts index 703935dc8..056e1f251 100644 --- a/extensions/ql-vscode/src/query-server/queryserver-client.ts +++ b/extensions/ql-vscode/src/query-server/queryserver-client.ts @@ -6,7 +6,7 @@ import { CancellationToken, commands } from "vscode"; import { createMessageConnection, RequestType } from "vscode-jsonrpc/node"; import * as cli from "../cli"; import { QueryServerConfig } from "../config"; -import { Logger, ProgressReporter } from "../logging"; +import { Logger, ProgressReporter } from "../common"; import { progress, ProgressMessage, diff --git a/extensions/ql-vscode/src/query-server/run-queries.ts b/extensions/ql-vscode/src/query-server/run-queries.ts index ad333caf9..99f1be795 100644 --- a/extensions/ql-vscode/src/query-server/run-queries.ts +++ b/extensions/ql-vscode/src/query-server/run-queries.ts @@ -9,7 +9,7 @@ import { showAndLogWarningMessage, tryGetQueryMetadata, } from "../helpers"; -import { logger } from "../logging"; +import { logger } from "../common"; import * as messages from "../pure/new-messages"; import * as legacyMessages from "../pure/legacy-messages"; import { InitialQueryInfo, LocalQueryInfo } from "../query-results"; diff --git a/extensions/ql-vscode/src/remote-queries/analyses-results-manager.ts b/extensions/ql-vscode/src/remote-queries/analyses-results-manager.ts index b99f8df57..e69ea9be6 100644 --- a/extensions/ql-vscode/src/remote-queries/analyses-results-manager.ts +++ b/extensions/ql-vscode/src/remote-queries/analyses-results-manager.ts @@ -4,7 +4,7 @@ import * as path from "path"; import { CancellationToken, ExtensionContext } from "vscode"; import { Credentials } from "../authentication"; -import { Logger } from "../logging"; +import { Logger } from "../common"; import { downloadArtifactFromLink } from "./gh-api/gh-actions-api-client"; import { AnalysisSummary } from "./shared/remote-query-result"; import { diff --git a/extensions/ql-vscode/src/remote-queries/bqrs-processing.ts b/extensions/ql-vscode/src/remote-queries/bqrs-processing.ts index eeb036adf..6cbf302a8 100644 --- a/extensions/ql-vscode/src/remote-queries/bqrs-processing.ts +++ b/extensions/ql-vscode/src/remote-queries/bqrs-processing.ts @@ -1,5 +1,5 @@ import { CodeQLCliServer } from "../cli"; -import { Logger } from "../logging"; +import { Logger } from "../common"; import { transformBqrsResultSet } from "../pure/bqrs-cli-types"; import { AnalysisRawResults } from "./shared/analysis-result"; import { MAX_RAW_RESULTS } from "./shared/result-limits"; diff --git a/extensions/ql-vscode/src/remote-queries/export-results.ts b/extensions/ql-vscode/src/remote-queries/export-results.ts index e6abb3ff4..bdb83dfbe 100644 --- a/extensions/ql-vscode/src/remote-queries/export-results.ts +++ b/extensions/ql-vscode/src/remote-queries/export-results.ts @@ -12,7 +12,7 @@ import { import { Credentials } from "../authentication"; import { UserCancellationException } from "../commandRunner"; import { showInformationMessageWithAction } from "../helpers"; -import { logger } from "../logging"; +import { logger } from "../common"; import { QueryHistoryManager } from "../query-history"; import { createGist } from "./gh-api/gh-api-client"; import { RemoteQueriesManager } from "./remote-queries-manager"; diff --git a/extensions/ql-vscode/src/remote-queries/gh-api/gh-actions-api-client.ts b/extensions/ql-vscode/src/remote-queries/gh-api/gh-actions-api-client.ts index 2b712afb8..80b9ac320 100644 --- a/extensions/ql-vscode/src/remote-queries/gh-api/gh-actions-api-client.ts +++ b/extensions/ql-vscode/src/remote-queries/gh-api/gh-actions-api-client.ts @@ -6,7 +6,7 @@ import { tmpDir, } from "../../helpers"; import { Credentials } from "../../authentication"; -import { logger } from "../../logging"; +import { logger } from "../../common"; import { RemoteQueryWorkflowResult } from "../remote-query-workflow-result"; import { DownloadLink, createDownloadPath } from "../download-link"; import { RemoteQuery } from "../remote-query"; diff --git a/extensions/ql-vscode/src/remote-queries/remote-queries-manager.ts b/extensions/ql-vscode/src/remote-queries/remote-queries-manager.ts index 2806bd6e4..b57878c97 100644 --- a/extensions/ql-vscode/src/remote-queries/remote-queries-manager.ts +++ b/extensions/ql-vscode/src/remote-queries/remote-queries-manager.ts @@ -20,7 +20,7 @@ import { showAndLogInformationMessage, showInformationMessageWithAction, } from "../helpers"; -import { Logger } from "../logging"; +import { Logger } from "../common"; import { prepareRemoteQueryRun } from "./run-remote-query"; import { RemoteQueriesView } from "./remote-queries-view"; import { buildRemoteQueryEntity, RemoteQuery } from "./remote-query"; diff --git a/extensions/ql-vscode/src/remote-queries/remote-queries-monitor.ts b/extensions/ql-vscode/src/remote-queries/remote-queries-monitor.ts index 5b37599a8..475404414 100644 --- a/extensions/ql-vscode/src/remote-queries/remote-queries-monitor.ts +++ b/extensions/ql-vscode/src/remote-queries/remote-queries-monitor.ts @@ -1,6 +1,6 @@ import * as vscode from "vscode"; import { Credentials } from "../authentication"; -import { Logger } from "../logging"; +import { Logger } from "../common"; import { sleep } from "../pure/time"; import { getWorkflowStatus, diff --git a/extensions/ql-vscode/src/remote-queries/remote-queries-view.ts b/extensions/ql-vscode/src/remote-queries/remote-queries-view.ts index 792718e05..b82f1d153 100644 --- a/extensions/ql-vscode/src/remote-queries/remote-queries-view.ts +++ b/extensions/ql-vscode/src/remote-queries/remote-queries-view.ts @@ -14,7 +14,7 @@ import { RemoteQueryDownloadAnalysisResultsMessage, RemoteQueryDownloadAllAnalysesResultsMessage, } from "../pure/interface-types"; -import { Logger } from "../logging"; +import { Logger } from "../common"; import { assertNever } from "../pure/helpers-pure"; import { AnalysisSummary, diff --git a/extensions/ql-vscode/src/remote-queries/repository-selection.ts b/extensions/ql-vscode/src/remote-queries/repository-selection.ts index 786fdeb33..4551ed55c 100644 --- a/extensions/ql-vscode/src/remote-queries/repository-selection.ts +++ b/extensions/ql-vscode/src/remote-queries/repository-selection.ts @@ -1,6 +1,6 @@ import * as fs from "fs-extra"; import { QuickPickItem, window } from "vscode"; -import { logger } from "../logging"; +import { logger } from "../common"; import { getRemoteRepositoryLists, getRemoteRepositoryListsPath, diff --git a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts b/extensions/ql-vscode/src/remote-queries/run-remote-query.ts index 337048707..6b85fac07 100644 --- a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts +++ b/extensions/ql-vscode/src/remote-queries/run-remote-query.ts @@ -12,7 +12,7 @@ import { } from "../helpers"; import { Credentials } from "../authentication"; import * as cli from "../cli"; -import { logger } from "../logging"; +import { logger } from "../common"; import { getActionBranch, getRemoteControllerRepo, diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-results-manager.ts b/extensions/ql-vscode/src/remote-queries/variant-analysis-results-manager.ts index 00f245597..a97b21674 100644 --- a/extensions/ql-vscode/src/remote-queries/variant-analysis-results-manager.ts +++ b/extensions/ql-vscode/src/remote-queries/variant-analysis-results-manager.ts @@ -3,7 +3,7 @@ import * as os from "os"; import * as path from "path"; import { Credentials } from "../authentication"; -import { Logger } from "../logging"; +import { Logger } from "../common"; import { AnalysisAlert, AnalysisRawResults } from "./shared/analysis-result"; import { sarifParser } from "../sarif-parser"; import { extractAnalysisAlerts } from "./sarif-processing"; diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts b/extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts index 0a10f43a9..1cad53299 100644 --- a/extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts +++ b/extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts @@ -8,7 +8,7 @@ import { } from "vscode"; import { URLSearchParams } from "url"; import { AbstractWebview, WebviewPanelConfig } from "../abstract-webview"; -import { logger } from "../logging"; +import { logger } from "../common"; import { FromVariantAnalysisMessage, ToVariantAnalysisMessage, diff --git a/extensions/ql-vscode/src/run-queries-shared.ts b/extensions/ql-vscode/src/run-queries-shared.ts index 4759107c6..c81449565 100644 --- a/extensions/ql-vscode/src/run-queries-shared.ts +++ b/extensions/ql-vscode/src/run-queries-shared.ts @@ -25,7 +25,8 @@ import { CodeQLCliServer } from "./cli"; import { SELECT_QUERY_NAME } from "./contextual/locationFinder"; import { DatabaseManager } from "./databases"; import { DecodedBqrsChunk } from "./pure/bqrs-cli-types"; -import { logger, Logger } from "./logging"; +import { logger } from "./common"; +import { Logger } from "./common"; import { generateSummarySymbolsFile } from "./log-insights/summary-parser"; import { asError } from "./pure/helpers-pure"; diff --git a/extensions/ql-vscode/src/telemetry.ts b/extensions/ql-vscode/src/telemetry.ts index 6bde1c447..54bdab469 100644 --- a/extensions/ql-vscode/src/telemetry.ts +++ b/extensions/ql-vscode/src/telemetry.ts @@ -14,7 +14,7 @@ import { isIntegrationTestMode, } from "./config"; import * as appInsights from "applicationinsights"; -import { logger } from "./logging"; +import { logger } from "./common"; import { UserCancellationException } from "./commandRunner"; import { showBinaryChoiceWithUrlDialog } from "./helpers"; diff --git a/extensions/ql-vscode/src/test-adapter.ts b/extensions/ql-vscode/src/test-adapter.ts index c6921c6fa..531879fec 100644 --- a/extensions/ql-vscode/src/test-adapter.ts +++ b/extensions/ql-vscode/src/test-adapter.ts @@ -33,7 +33,7 @@ import { showAndLogErrorMessage, showAndLogWarningMessage, } from "./helpers"; -import { testLogger } from "./logging"; +import { testLogger } from "./common"; import { DatabaseItem, DatabaseManager } from "./databases"; /** diff --git a/extensions/ql-vscode/src/test-ui.ts b/extensions/ql-vscode/src/test-ui.ts index 79dcfc124..c0bcc99c4 100644 --- a/extensions/ql-vscode/src/test-ui.ts +++ b/extensions/ql-vscode/src/test-ui.ts @@ -16,7 +16,7 @@ import { TestTreeNode } from "./test-tree-node"; import { DisposableObject } from "./pure/disposable-object"; import { UIService } from "./vscode-utils/ui-service"; import { QLTestAdapter, getExpectedFile, getActualFile } from "./test-adapter"; -import { logger } from "./logging"; +import { logger } from "./common"; type VSCodeTestEvent = | TestRunStartedEvent diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts index 35681f003..35e257d38 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts @@ -11,7 +11,7 @@ import { extensions } from "vscode"; import { CodeQLExtensionInterface } from "../../extension"; import { describeWithCodeQL } from "../cli"; import { QueryServerClient } from "../../legacy-query-server/queryserver-client"; -import { logger, ProgressReporter } from "../../logging"; +import { logger, ProgressReporter } from "../../common"; const baseDir = path.join(__dirname, "../../../test/data"); diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts index 1cc736d0e..456bce7b6 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts @@ -9,7 +9,7 @@ import { extensions, Uri } from "vscode"; import { CodeQLExtensionInterface } from "../../extension"; import { describeWithCodeQL } from "../cli"; import { QueryServerClient } from "../../query-server/queryserver-client"; -import { logger, ProgressReporter } from "../../logging"; +import { logger, ProgressReporter } from "../../common"; import { QueryResultType } from "../../pure/new-messages"; import { cleanDatabases, dbLoc, storagePath } from "./global.helper"; import { importArchiveDatabase } from "../../databaseFetcher"; diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts index 6adb83d51..85e4ddfd3 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/remote-queries-manager.test.ts @@ -21,7 +21,7 @@ import { UserCancellationException } from "../../../commandRunner"; import * as ghApiClient from "../../../remote-queries/gh-api/gh-api-client"; import { Repository } from "../../../remote-queries/gh-api/repository"; import { createMockExtensionContext } from "../../no-workspace"; -import { OutputChannelLogger } from "../../../logging"; +import { OutputChannelLogger } from "../../../common"; import { RemoteQueriesSubmission } from "../../../remote-queries/shared/remote-queries"; import { readBundledPack } from "../../utils/bundled-pack-helpers"; import { RemoteQueriesManager } from "../../../remote-queries/remote-queries-manager"; diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts index 9a814b50b..37d979ddb 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts @@ -8,7 +8,7 @@ import { window, } from "vscode"; import { CodeQLExtensionInterface } from "../../../extension"; -import { logger } from "../../../logging"; +import { logger } from "../../../common"; import * as config from "../../../config"; import { setRemoteControllerRepo, diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts index 4458047e8..f77229f65 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts @@ -1,6 +1,6 @@ import { extensions } from "vscode"; import { CodeQLExtensionInterface } from "../../../extension"; -import { logger } from "../../../logging"; +import { logger } from "../../../common"; import { Credentials } from "../../../authentication"; import * as fs from "fs-extra"; import * as path from "path"; diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases.test.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases.test.ts index b0b125199..116e4e1dc 100644 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases.test.ts @@ -11,7 +11,7 @@ import { FullDatabaseOptions, findSourceArchive, } from "../../databases"; -import { Logger } from "../../logging"; +import { Logger } from "../../common"; import { ProgressCallback } from "../../commandRunner"; import { CodeQLCliServer, DbInfo } from "../../cli"; import { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts index 8712119ef..25cc3a42c 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts @@ -3,7 +3,7 @@ import * as fetch from "node-fetch"; import * as semver from "semver"; import * as helpers from "../../helpers"; -import { logger } from "../../logging"; +import { logger } from "../../common"; import * as fs from "fs-extra"; import * as os from "os"; import { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts index 7a2fb1b43..48f010b14 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts @@ -2,7 +2,7 @@ import * as fs from "fs-extra"; import * as path from "path"; import * as vscode from "vscode"; -import { logger } from "../../logging"; +import { logger } from "../../common"; import { registerQueryHistoryScrubber } from "../../query-history-scrubber"; import { HistoryTreeDataProvider, diff --git a/extensions/ql-vscode/test/pure-tests/logging.test.ts b/extensions/ql-vscode/test/common/logging/output-channel-logger.test.ts similarity index 97% rename from extensions/ql-vscode/test/pure-tests/logging.test.ts rename to extensions/ql-vscode/test/common/logging/output-channel-logger.test.ts index acc8011c2..dd54b3bff 100644 --- a/extensions/ql-vscode/test/pure-tests/logging.test.ts +++ b/extensions/ql-vscode/test/common/logging/output-channel-logger.test.ts @@ -1,7 +1,7 @@ import * as fs from "fs-extra"; import * as path from "path"; import * as tmp from "tmp"; -import { OutputChannelLogger } from "../../src/logging"; +import { OutputChannelLogger } from "../../../src/common"; jest.setTimeout(999999); From 45ffd8a45c3a5426910ac59efaab75aba32ef871 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Wed, 30 Nov 2022 09:59:44 +0000 Subject: [PATCH 54/58] Tidy up logging docs --- .../ql-vscode/src/common/logging/logger.ts | 23 ++++++++++++++----- .../src/common/logging/vscode/loggers.ts | 12 ++++++---- .../logging/vscode/output-channel-logger.ts | 14 ++++------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/extensions/ql-vscode/src/common/logging/logger.ts b/extensions/ql-vscode/src/common/logging/logger.ts index b66cb1054..89fc1e54b 100644 --- a/extensions/ql-vscode/src/common/logging/logger.ts +++ b/extensions/ql-vscode/src/common/logging/logger.ts @@ -1,23 +1,34 @@ export interface LogOptions { - /** If false, don't output a trailing newline for the log entry. Default true. */ + // If false, don't output a trailing newline for the log entry. Default true. trailingNewline?: boolean; - /** If specified, add this log entry to the log file at the specified location. */ + // If specified, add this log entry to the log file at the specified location. additionalLogLocation?: string; } export interface Logger { - /** Writes the given log message, optionally followed by a newline. */ - log(message: string, options?: LogOptions): Promise; /** - * Reveal this channel in the UI. + * Writes the given log message, optionally followed by a newline. + * This function is asynchronous and will only resolve once the message is written + * to the side log (if required). It is not necessary to await the results of this + * function if you don't need to guarantee that the log writing is complete before + * continuing. + * + * @param message The message to log. + * @param options Optional settings. + */ + log(message: string, options?: LogOptions): Promise; + + /** + * Reveal the logger channel in the UI. * * @param preserveFocus When `true` the channel will not take focus. */ show(preserveFocus?: boolean): void; /** - * Remove the log at the specified location + * Remove the log at the specified location. + * * @param location log to remove */ removeAdditionalLogLocation(location: string | undefined): void; diff --git a/extensions/ql-vscode/src/common/logging/vscode/loggers.ts b/extensions/ql-vscode/src/common/logging/vscode/loggers.ts index abebd2fa5..af7eb5258 100644 --- a/extensions/ql-vscode/src/common/logging/vscode/loggers.ts +++ b/extensions/ql-vscode/src/common/logging/vscode/loggers.ts @@ -1,15 +1,19 @@ +/** + * This module contains instantiated loggers to use in the extension. + */ + import { OutputChannelLogger } from "./output-channel-logger"; -/** The global logger for the extension. */ +// Global logger for the extension. export const logger = new OutputChannelLogger("CodeQL Extension Log"); -/** The logger for messages from the query server. */ +// Logger for messages from the query server. export const queryServerLogger = new OutputChannelLogger("CodeQL Query Server"); -/** The logger for messages from the language server. */ +// Logger for messages from the language server. export const ideServerLogger = new OutputChannelLogger( "CodeQL Language Server", ); -/** The logger for messages from tests. */ +// Logger for messages from tests. export const testLogger = new OutputChannelLogger("CodeQL Tests"); diff --git a/extensions/ql-vscode/src/common/logging/vscode/output-channel-logger.ts b/extensions/ql-vscode/src/common/logging/vscode/output-channel-logger.ts index c8b37bfbe..f4646a0f5 100644 --- a/extensions/ql-vscode/src/common/logging/vscode/output-channel-logger.ts +++ b/extensions/ql-vscode/src/common/logging/vscode/output-channel-logger.ts @@ -4,7 +4,9 @@ import * as path from "path"; import { Logger, LogOptions } from "../logger"; import { DisposableObject } from "../../../pure/disposable-object"; -/** A logger that writes messages to an output channel in the Output tab. */ +/** + * A logger that writes messages to an output channel in the VS Code Output tab. + */ export class OutputChannelLogger extends DisposableObject implements Logger { public readonly outputChannel: OutputChannel; private readonly additionalLocations = new Map< @@ -20,12 +22,6 @@ export class OutputChannelLogger extends DisposableObject implements Logger { this.isCustomLogDirectory = false; } - /** - * This function is asynchronous and will only resolve once the message is written - * to the side log (if required). It is not necessary to await the results of this - * function if you don't need to guarantee that the log writing is complete before - * continuing. - */ async log(message: string, options = {} as LogOptions): Promise { try { if (options.trailingNewline === undefined) { @@ -82,9 +78,7 @@ export class OutputChannelLogger extends DisposableObject implements Logger { } class AdditionalLogLocation { - constructor(private location: string) { - /**/ - } + constructor(private location: string) {} async log(message: string, options = {} as LogOptions): Promise { if (options.trailingNewline === undefined) { From c889c0958407779d5ad151e10c72af2f1f42e991 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Wed, 30 Nov 2022 13:53:17 +0000 Subject: [PATCH 55/58] Rename logger to extLogger --- .../src/archive-filesystem-provider.ts | 6 +- extensions/ql-vscode/src/commandRunner.ts | 6 +- .../src/common/logging/vscode/loggers.ts | 2 +- extensions/ql-vscode/src/config.ts | 4 +- .../ql-vscode/src/contextual/queryResolver.ts | 12 ++-- extensions/ql-vscode/src/databaseFetcher.ts | 6 +- extensions/ql-vscode/src/databases-ui.ts | 12 ++-- extensions/ql-vscode/src/databases.ts | 16 ++--- .../ql-vscode/src/databases/db-module.ts | 4 +- extensions/ql-vscode/src/discovery.ts | 4 +- extensions/ql-vscode/src/distribution.ts | 25 ++++--- extensions/ql-vscode/src/extension.ts | 71 ++++++++++--------- extensions/ql-vscode/src/helpers.ts | 22 +++--- .../src/legacy-query-server/run-queries.ts | 12 ++-- .../src/legacy-query-server/upgrades.ts | 6 +- .../src/log-insights/log-scanner-service.ts | 4 +- .../log-insights/summary-language-support.ts | 4 +- extensions/ql-vscode/src/packaging.ts | 4 +- .../ql-vscode/src/query-history-scrubber.ts | 22 +++--- extensions/ql-vscode/src/query-history.ts | 22 +++--- .../ql-vscode/src/query-server/run-queries.ts | 4 +- .../src/remote-queries/export-results.ts | 10 +-- .../gh-api/gh-actions-api-client.ts | 6 +- .../remote-queries/repository-selection.ts | 10 +-- .../src/remote-queries/run-remote-query.ts | 18 ++--- .../remote-queries/variant-analysis-view.ts | 4 +- .../ql-vscode/src/run-queries-shared.ts | 6 +- extensions/ql-vscode/src/telemetry.ts | 4 +- extensions/ql-vscode/src/test-ui.ts | 4 +- .../cli-integration/legacy-query.test.ts | 4 +- .../cli-integration/new-query.test.ts | 4 +- .../variant-analysis-manager.test.ts | 6 +- .../variant-analysis-results-manager.test.ts | 6 +- .../no-workspace/distribution.test.ts | 6 +- .../no-workspace/query-history.test.ts | 4 +- 35 files changed, 184 insertions(+), 176 deletions(-) diff --git a/extensions/ql-vscode/src/archive-filesystem-provider.ts b/extensions/ql-vscode/src/archive-filesystem-provider.ts index 8681d681a..6ef1b6d7d 100644 --- a/extensions/ql-vscode/src/archive-filesystem-provider.ts +++ b/extensions/ql-vscode/src/archive-filesystem-provider.ts @@ -1,7 +1,7 @@ import * as fs from "fs-extra"; import * as unzipper from "unzipper"; import * as vscode from "vscode"; -import { logger } from "./common"; +import { extLogger } from "./common"; // All path operations in this file must be on paths *within* the zip // archive. @@ -118,7 +118,7 @@ class InvalidSourceArchiveUriError extends Error { export function decodeSourceArchiveUri(uri: vscode.Uri): ZipFileReference { if (!uri.authority) { // Uri is malformed, but this is recoverable - void logger.log( + void extLogger.log( `Warning: ${new InvalidSourceArchiveUriError(uri).message}`, ); return { @@ -148,7 +148,7 @@ function ensureFile(map: DirectoryHierarchyMap, file: string) { const dirname = path.dirname(file); if (dirname === ".") { const error = `Ill-formed path ${file} in zip archive (expected absolute path)`; - void logger.log(error); + void extLogger.log(error); throw new Error(error); } ensureDir(map, dirname); diff --git a/extensions/ql-vscode/src/commandRunner.ts b/extensions/ql-vscode/src/commandRunner.ts index a0881d4b4..8b9fa1013 100644 --- a/extensions/ql-vscode/src/commandRunner.ts +++ b/extensions/ql-vscode/src/commandRunner.ts @@ -7,7 +7,7 @@ import { ProgressLocation, } from "vscode"; import { showAndLogErrorMessage, showAndLogWarningMessage } from "./helpers"; -import { logger } from "./common"; +import { extLogger } from "./common"; import { getErrorMessage, getErrorStack } from "./pure/helpers-pure"; import { telemetryListener } from "./telemetry"; @@ -131,7 +131,7 @@ export function commandRunner( if (e instanceof UserCancellationException) { // User has cancelled this action manually if (e.silent) { - void logger.log(errorMessage); + void extLogger.log(errorMessage); } else { void showAndLogWarningMessage(errorMessage); } @@ -166,7 +166,7 @@ export function commandRunnerWithProgress( commandId: string, task: ProgressTask, progressOptions: Partial, - outputLogger = logger, + outputLogger = extLogger, ): Disposable { return commands.registerCommand(commandId, async (...args: any[]) => { const startTime = Date.now(); diff --git a/extensions/ql-vscode/src/common/logging/vscode/loggers.ts b/extensions/ql-vscode/src/common/logging/vscode/loggers.ts index af7eb5258..ff91b00fb 100644 --- a/extensions/ql-vscode/src/common/logging/vscode/loggers.ts +++ b/extensions/ql-vscode/src/common/logging/vscode/loggers.ts @@ -5,7 +5,7 @@ import { OutputChannelLogger } from "./output-channel-logger"; // Global logger for the extension. -export const logger = new OutputChannelLogger("CodeQL Extension Log"); +export const extLogger = new OutputChannelLogger("CodeQL Extension Log"); // Logger for messages from the query server. export const queryServerLogger = new OutputChannelLogger("CodeQL Query Server"); diff --git a/extensions/ql-vscode/src/config.ts b/extensions/ql-vscode/src/config.ts index 5ac442afe..56b2fc1e5 100644 --- a/extensions/ql-vscode/src/config.ts +++ b/extensions/ql-vscode/src/config.ts @@ -7,7 +7,7 @@ import { ConfigurationTarget, } from "vscode"; import { DistributionManager } from "./distribution"; -import { logger } from "./common"; +import { extLogger } from "./common"; import { ONE_DAY_IN_MS } from "./pure/time"; export const ALL_SETTINGS: Setting[] = []; @@ -349,7 +349,7 @@ export class QueryServerConfigListener return undefined; } if (memory == 0 || typeof memory !== "number") { - void logger.log( + void extLogger.log( `Ignoring value '${memory}' for setting ${MEMORY_SETTING.qualifiedName}`, ); return undefined; diff --git a/extensions/ql-vscode/src/contextual/queryResolver.ts b/extensions/ql-vscode/src/contextual/queryResolver.ts index ecb2f30d7..e3be499e5 100644 --- a/extensions/ql-vscode/src/contextual/queryResolver.ts +++ b/extensions/ql-vscode/src/contextual/queryResolver.ts @@ -8,7 +8,7 @@ import { KeyType, kindOfKeyType, nameOfKeyType, tagOfKeyType } from "./keyType"; import { CodeQLCliServer } from "../cli"; import { DatabaseItem } from "../databases"; import { QlPacksForLanguage } from "../helpers"; -import { logger } from "../common"; +import { extLogger } from "../common"; import { createInitialQueryInfo } from "../run-queries-shared"; import { CancellationToken, Uri } from "vscode"; import { ProgressCallback } from "../commandRunner"; @@ -161,17 +161,17 @@ async function resolveContextualQuery( // No lock file, likely because this library pack is in the package cache. // Create a lock file so that we can resolve dependencies and library path // for the contextual query. - void logger.log( + void extLogger.log( `Library pack ${packPath} is missing a lock file; creating a temporary lock file`, ); await cli.packResolveDependencies(packPath); createdTempLockFile = true; // Clear CLI server pack cache before installing dependencies, // so that it picks up the new lock file, not the previously cached pack. - void logger.log("Clearing the CodeQL CLI server's pack cache"); + void extLogger.log("Clearing the CodeQL CLI server's pack cache"); await cli.clearCache(); // Install dependencies. - void logger.log( + void extLogger.log( `Installing package dependencies for library pack ${packPath}`, ); await cli.packInstall(packPath); @@ -181,7 +181,7 @@ async function resolveContextualQuery( async function removeTemporaryLockFile(packPath: string) { const tempLockFilePath = path.resolve(packPath, "codeql-pack.lock.yml"); - void logger.log( + void extLogger.log( `Deleting temporary package lock file at ${tempLockFilePath}`, ); // It's fine if the file doesn't exist. @@ -212,7 +212,7 @@ export async function runContextualQuery( }, false, ); - void logger.log( + void extLogger.log( `Running contextual query ${query}; results will be stored in ${queryStorageDir}`, ); const queryResult = await qs.compileAndRunQueryAgainstDatabase( diff --git a/extensions/ql-vscode/src/databaseFetcher.ts b/extensions/ql-vscode/src/databaseFetcher.ts index 40d5a9546..4415d01c0 100644 --- a/extensions/ql-vscode/src/databaseFetcher.ts +++ b/extensions/ql-vscode/src/databaseFetcher.ts @@ -11,7 +11,7 @@ import { retry } from "@octokit/plugin-retry"; import { DatabaseManager, DatabaseItem } from "./databases"; import { showAndLogInformationMessage } from "./helpers"; import { reportStreamProgress, ProgressCallback } from "./commandRunner"; -import { logger } from "./common"; +import { extLogger } from "./common"; import { tmpDir } from "./helpers"; import { Credentials } from "./authentication"; import { REPO_REGEX, getErrorMessage } from "./pure/helpers-pure"; @@ -585,7 +585,7 @@ export async function convertGithubNwoToDatabaseUrl( name: repo, }; } catch (e) { - void logger.log(`Error: ${getErrorMessage(e)}`); + void extLogger.log(`Error: ${getErrorMessage(e)}`); throw new Error(`Unable to get database for '${githubRepo}'`); } } @@ -696,7 +696,7 @@ export async function convertLgtmUrlToDatabaseUrl( language, ].join("/")}`; } catch (e) { - void logger.log(`Error: ${getErrorMessage(e)}`); + void extLogger.log(`Error: ${getErrorMessage(e)}`); throw new Error(`Invalid LGTM URL: ${lgtmUrl}`); } } diff --git a/extensions/ql-vscode/src/databases-ui.ts b/extensions/ql-vscode/src/databases-ui.ts index 902ba0532..fcf450439 100644 --- a/extensions/ql-vscode/src/databases-ui.ts +++ b/extensions/ql-vscode/src/databases-ui.ts @@ -27,7 +27,7 @@ import { isLikelyDbLanguageFolder, showAndLogErrorMessage, } from "./helpers"; -import { logger } from "./common"; +import { extLogger } from "./common"; import { importArchiveDatabase, promptImportGithubDatabase, @@ -241,7 +241,7 @@ export class DatabaseUI extends DisposableObject { } init() { - void logger.log("Registering database panel commands."); + void extLogger.log("Registering database panel commands."); this.push( commandRunnerWithProgress( "codeQL.setCurrentDatabase", @@ -393,14 +393,14 @@ export class DatabaseUI extends DisposableObject { }; handleRemoveOrphanedDatabases = async (): Promise => { - void logger.log("Removing orphaned databases from workspace storage."); + void extLogger.log("Removing orphaned databases from workspace storage."); let dbDirs = undefined; if ( !(await fs.pathExists(this.storagePath)) || !(await fs.stat(this.storagePath)).isDirectory() ) { - void logger.log( + void extLogger.log( "Missing or invalid storage directory. Not trying to remove orphaned databases.", ); return; @@ -425,7 +425,7 @@ export class DatabaseUI extends DisposableObject { dbDirs = await asyncFilter(dbDirs, isLikelyDatabaseRoot); if (!dbDirs.length) { - void logger.log("No orphaned databases found."); + void extLogger.log("No orphaned databases found."); return; } @@ -434,7 +434,7 @@ export class DatabaseUI extends DisposableObject { await Promise.all( dbDirs.map(async (dbDir) => { try { - void logger.log(`Deleting orphaned database '${dbDir}'.`); + void extLogger.log(`Deleting orphaned database '${dbDir}'.`); await fs.remove(dbDir); } catch (e) { failures.push(`${path.basename(dbDir)}`); diff --git a/extensions/ql-vscode/src/databases.ts b/extensions/ql-vscode/src/databases.ts index f27b43f4d..8c4f116ca 100644 --- a/extensions/ql-vscode/src/databases.ts +++ b/extensions/ql-vscode/src/databases.ts @@ -18,7 +18,7 @@ import { encodeSourceArchiveUri, } from "./archive-filesystem-provider"; import { DisposableObject } from "./pure/disposable-object"; -import { Logger, logger } from "./common"; +import { Logger, extLogger } from "./common"; import { getErrorMessage } from "./pure/helpers-pure"; import { QueryRunner } from "./queryRunner"; @@ -545,7 +545,7 @@ function eventFired( ): Promise { return new Promise((res, _rej) => { const timeout = setTimeout(() => { - void logger.log( + void extLogger.log( `Waiting for event ${event} timed out after ${timeoutMs}ms`, ); res(undefined); @@ -657,12 +657,12 @@ export class DatabaseManager extends DisposableObject { const msg = item.verifyZippedSources(); if (msg) { - void logger.log(`Could not add source folder because ${msg}`); + void extLogger.log(`Could not add source folder because ${msg}`); return; } const uri = item.getSourceArchiveExplorerUri(); - void logger.log( + void extLogger.log( `Adding workspace folder for ${item.name} source archive at index ${end}`, ); if ((vscode.workspace.workspaceFolders || []).length < 2) { @@ -916,7 +916,7 @@ export class DatabaseManager extends DisposableObject { (folder) => item.belongsToSourceArchiveExplorerUri(folder.uri), ); if (folderIndex >= 0) { - void logger.log(`Removing workspace folder at index ${folderIndex}`); + void extLogger.log(`Removing workspace folder at index ${folderIndex}`); vscode.workspace.updateWorkspaceFolders(folderIndex, 1); } @@ -925,11 +925,11 @@ export class DatabaseManager extends DisposableObject { // Delete folder from file system only if it is controlled by the extension if (this.isExtensionControlledLocation(item.databaseUri)) { - void logger.log("Deleting database from filesystem."); + void extLogger.log("Deleting database from filesystem."); fs.remove(item.databaseUri.fsPath).then( - () => void logger.log(`Deleted '${item.databaseUri.fsPath}'`), + () => void extLogger.log(`Deleted '${item.databaseUri.fsPath}'`), (e) => - void logger.log( + void extLogger.log( `Failed to delete '${ item.databaseUri.fsPath }'. Reason: ${getErrorMessage(e)}`, diff --git a/extensions/ql-vscode/src/databases/db-module.ts b/extensions/ql-vscode/src/databases/db-module.ts index d7e08bfa4..95348525a 100644 --- a/extensions/ql-vscode/src/databases/db-module.ts +++ b/extensions/ql-vscode/src/databases/db-module.ts @@ -1,6 +1,6 @@ import { App, AppMode } from "../common/app"; import { isCanary, isNewQueryRunExperienceEnabled } from "../config"; -import { logger } from "../common"; +import { extLogger } from "../common"; import { DisposableObject } from "../pure/disposable-object"; import { DbConfigStore } from "./config/db-config-store"; import { DbManager } from "./db-manager"; @@ -19,7 +19,7 @@ export class DbModule extends DisposableObject { return; } - void logger.log("Initializing database module"); + void extLogger.log("Initializing database module"); const dbConfigStore = new DbConfigStore(app); await dbConfigStore.initialize(); diff --git a/extensions/ql-vscode/src/discovery.ts b/extensions/ql-vscode/src/discovery.ts index 95baaa2dc..117df4fd9 100644 --- a/extensions/ql-vscode/src/discovery.ts +++ b/extensions/ql-vscode/src/discovery.ts @@ -1,5 +1,5 @@ import { DisposableObject } from "./pure/disposable-object"; -import { logger } from "./common"; +import { extLogger } from "./common"; /** * Base class for "discovery" operations, which scan the file system to find specific kinds of @@ -62,7 +62,7 @@ export abstract class Discovery extends DisposableObject { }) .catch((err) => { - void logger.log(`${this.name} failed. Reason: ${err.message}`); + void extLogger.log(`${this.name} failed. Reason: ${err.message}`); }) .finally(() => { diff --git a/extensions/ql-vscode/src/distribution.ts b/extensions/ql-vscode/src/distribution.ts index 2e9563296..68ad27ed0 100644 --- a/extensions/ql-vscode/src/distribution.ts +++ b/extensions/ql-vscode/src/distribution.ts @@ -12,7 +12,7 @@ import { showAndLogErrorMessage, showAndLogWarningMessage, } from "./helpers"; -import { logger } from "./common"; +import { extLogger } from "./common"; import { getCodeQlCliVersion } from "./cli-version"; import { ProgressCallback, reportStreamProgress } from "./commandRunner"; import { @@ -90,7 +90,10 @@ export class DistributionManager implements DistributionProvider { kind: FindDistributionResultKind.NoDistribution, }; } - const version = await getCodeQlCliVersion(distribution.codeQlPath, logger); + const version = await getCodeQlCliVersion( + distribution.codeQlPath, + extLogger, + ); if (version === undefined) { return { distribution, @@ -196,7 +199,7 @@ export class DistributionManager implements DistributionProvider { }; } } - void logger.log("INFO: Could not find CodeQL on path."); + void extLogger.log("INFO: Could not find CodeQL on path."); } return undefined; @@ -292,7 +295,7 @@ class ExtensionSpecificDistributionManager { try { await this.removeDistribution(); } catch (e) { - void logger.log( + void extLogger.log( "WARNING: Tried to remove corrupted CodeQL CLI at " + `${this.getDistributionStoragePath()} but encountered an error: ${e}.`, ); @@ -343,7 +346,7 @@ class ExtensionSpecificDistributionManager { try { await this.removeDistribution(); } catch (e) { - void logger.log( + void extLogger.log( `Tried to clean up old version of CLI at ${this.getDistributionStoragePath()} ` + `but encountered an error: ${e}.`, ); @@ -360,7 +363,7 @@ class ExtensionSpecificDistributionManager { ); } if (assets.length > 1) { - void logger.log( + void extLogger.log( "WARNING: chose a release with more than one asset to install, found " + assets.map((asset) => asset.name).join(", "), ); @@ -398,7 +401,7 @@ class ExtensionSpecificDistributionManager { await this.bumpDistributionFolderIndex(); - void logger.log( + void extLogger.log( `Extracting CodeQL CLI to ${this.getDistributionStoragePath()}`, ); await extractZipArchive(archivePath, this.getDistributionStoragePath()); @@ -421,7 +424,7 @@ class ExtensionSpecificDistributionManager { private async getLatestRelease(): Promise { const requiredAssetName = getRequiredAssetName(); - void logger.log( + void extLogger.log( `Searching for latest release including ${requiredAssetName}.`, ); return this.createReleasesApiConsumer().getLatestRelease( @@ -433,13 +436,13 @@ class ExtensionSpecificDistributionManager { ); if (matchingAssets.length === 0) { // For example, this could be a release with no platform-specific assets. - void logger.log( + void extLogger.log( `INFO: Ignoring a release with no assets named ${requiredAssetName}`, ); return false; } if (matchingAssets.length > 1) { - void logger.log( + void extLogger.log( `WARNING: Ignoring a release with more than one asset named ${requiredAssetName}`, ); return false; @@ -817,7 +820,7 @@ export async function getExecutableFromDirectory( return alternateExpectedLauncherPath; } if (warnWhenNotFound) { - void logger.log( + void extLogger.log( `WARNING: Expected to find a CodeQL CLI executable at ${expectedLauncherPath} but one was not found. ` + "Will try PATH.", ); diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 0a77389fd..4b9584896 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -76,7 +76,7 @@ import { ResultsView } from "./interface"; import { WebviewReveal } from "./interface-utils"; import { ideServerLogger, - logger, + extLogger, ProgressReporter, queryServerLogger, } from "./common"; @@ -230,7 +230,7 @@ const MIN_VERSION = "1.67.0"; export async function activate( ctx: ExtensionContext, ): Promise> { - void logger.log(`Starting ${extensionId} extension`); + void extLogger.log(`Starting ${extensionId} extension`); if (extension === undefined) { throw new Error(`Can't find extension ${extensionId}`); } @@ -278,7 +278,7 @@ export async function activate( const minSecondsSinceLastUpdateCheck = config.isUserInitiated ? 0 : 86400; const noUpdatesLoggingFunc = config.shouldDisplayMessageWhenNoUpdates ? showAndLogInformationMessage - : async (message: string) => void logger.log(message); + : async (message: string) => void extLogger.log(message); const result = await distributionManager.checkForUpdatesToExtensionManagedDistribution( minSecondsSinceLastUpdateCheck, @@ -291,7 +291,7 @@ export async function activate( switch (result.kind) { case DistributionUpdateCheckResultKind.AlreadyCheckedRecentlyResult: - void logger.log( + void extLogger.log( "Didn't perform CodeQL CLI update check since a check was already performed within the previous " + `${minSecondsSinceLastUpdateCheck} seconds.`, ); @@ -400,7 +400,7 @@ export async function activate( const result = await distributionManager.getDistribution(); switch (result.kind) { case FindDistributionResultKind.CompatibleDistribution: - void logger.log( + void extLogger.log( `Found compatible version of CodeQL CLI (version ${result.version.raw})`, ); break; @@ -543,18 +543,18 @@ async function activateWithInstalledDistribution( // of activation. errorStubs.forEach((stub) => stub.dispose()); - void logger.log("Initializing configuration listener..."); + void extLogger.log("Initializing configuration listener..."); const qlConfigurationListener = await QueryServerConfigListener.createQueryServerConfigListener( distributionManager, ); ctx.subscriptions.push(qlConfigurationListener); - void logger.log("Initializing CodeQL cli server..."); + void extLogger.log("Initializing CodeQL cli server..."); const cliServer = new CodeQLCliServer( distributionManager, new CliConfigListener(), - logger, + extLogger, ); ctx.subscriptions.push(cliServer); @@ -564,7 +564,7 @@ async function activateWithInstalledDistribution( ); ctx.subscriptions.push(statusBar); - void logger.log("Initializing query server client."); + void extLogger.log("Initializing query server client."); const qs = await createQueryServer(qlConfigurationListener, cliServer, ctx); for (const glob of PACK_GLOBS) { @@ -575,14 +575,14 @@ async function activateWithInstalledDistribution( }); } - void logger.log("Initializing database manager."); - const dbm = new DatabaseManager(ctx, qs, cliServer, logger); + void extLogger.log("Initializing database manager."); + const dbm = new DatabaseManager(ctx, qs, cliServer, extLogger); // Let this run async. void dbm.loadPersistedState(); ctx.subscriptions.push(dbm); - void logger.log("Initializing database panel."); + void extLogger.log("Initializing database panel."); const databaseUI = new DatabaseUI( dbm, qs, @@ -593,11 +593,11 @@ async function activateWithInstalledDistribution( databaseUI.init(); ctx.subscriptions.push(databaseUI); - void logger.log("Initializing evaluator log viewer."); + void extLogger.log("Initializing evaluator log viewer."); const evalLogViewer = new EvalLogViewer(); ctx.subscriptions.push(evalLogViewer); - void logger.log("Initializing query history manager."); + void extLogger.log("Initializing query history manager."); const queryHistoryConfigurationListener = new QueryHistoryConfigListener(); ctx.subscriptions.push(queryHistoryConfigurationListener); const showResults = async (item: CompletedLocalQueryInfo) => @@ -608,7 +608,7 @@ async function activateWithInstalledDistribution( queryHistoryConfigurationListener, ); - void logger.log("Initializing results panel interface."); + void extLogger.log("Initializing results panel interface."); const localQueryResultsView = new ResultsView( ctx, dbm, @@ -618,7 +618,7 @@ async function activateWithInstalledDistribution( ); ctx.subscriptions.push(localQueryResultsView); - void logger.log("Initializing variant analysis manager."); + void extLogger.log("Initializing variant analysis manager."); const variantAnalysisStorageDir = path.join( ctx.globalStorageUri.fsPath, "variant-analyses", @@ -626,7 +626,7 @@ async function activateWithInstalledDistribution( await fs.ensureDir(variantAnalysisStorageDir); const variantAnalysisResultsManager = new VariantAnalysisResultsManager( cliServer, - logger, + extLogger, ); const variantAnalysisManager = new VariantAnalysisManager( ctx, @@ -643,11 +643,16 @@ async function activateWithInstalledDistribution( ), ); - void logger.log("Initializing remote queries manager."); - const rqm = new RemoteQueriesManager(ctx, cliServer, queryStorageDir, logger); + void extLogger.log("Initializing remote queries manager."); + const rqm = new RemoteQueriesManager( + ctx, + cliServer, + queryStorageDir, + extLogger, + ); ctx.subscriptions.push(rqm); - void logger.log("Initializing query history."); + void extLogger.log("Initializing query history."); const qhm = new QueryHistoryManager( qs, dbm, @@ -665,7 +670,7 @@ async function activateWithInstalledDistribution( ctx.subscriptions.push(qhm); - void logger.log("Initializing evaluation log scanners."); + void extLogger.log("Initializing evaluation log scanners."); const logScannerService = new LogScannerService(qhm); ctx.subscriptions.push(logScannerService); ctx.subscriptions.push( @@ -674,7 +679,7 @@ async function activateWithInstalledDistribution( ), ); - void logger.log("Initializing compare view."); + void extLogger.log("Initializing compare view."); const compareView = new CompareView( ctx, dbm, @@ -685,7 +690,7 @@ async function activateWithInstalledDistribution( ); ctx.subscriptions.push(compareView); - void logger.log("Initializing source archive filesystem provider."); + void extLogger.log("Initializing source archive filesystem provider."); archiveFilesystemProvider.activate(ctx); async function showResultsForComparison( @@ -821,7 +826,7 @@ async function activateWithInstalledDistribution( ctx.subscriptions.push(tmpDirDisposal); - void logger.log("Initializing CodeQL language server."); + void extLogger.log("Initializing CodeQL language server."); const client = new LanguageClient( "CodeQL Language Server", () => spawnIdeServer(qlConfigurationListener), @@ -839,7 +844,7 @@ async function activateWithInstalledDistribution( true, ); - void logger.log("Initializing QLTest interface."); + void extLogger.log("Initializing QLTest interface."); const testExplorerExtension = extensions.getExtension( testExplorerExtensionId, ); @@ -856,7 +861,7 @@ async function activateWithInstalledDistribution( ctx.subscriptions.push(testUIService); } - void logger.log("Registering top-level command palette commands."); + void extLogger.log("Registering top-level command palette commands."); ctx.subscriptions.push( commandRunnerWithProgress( "codeQL.runQuery", @@ -938,7 +943,7 @@ async function activateWithInstalledDistribution( } } if (skippedDatabases.length > 0) { - void logger.log(`Errors:\n${errors.join("\n")}`); + void extLogger.log(`Errors:\n${errors.join("\n")}`); void showAndLogWarningMessage( `The following databases were skipped:\n${skippedDatabases.join( "\n", @@ -1415,13 +1420,13 @@ async function activateWithInstalledDistribution( ctx.subscriptions.push( commandRunner("codeQL.showLogs", async () => { - logger.show(); + extLogger.show(); }), ); ctx.subscriptions.push(new SummaryLanguageSupport()); - void logger.log("Starting language server."); + void extLogger.log("Starting language server."); await client.start(); ctx.subscriptions.push({ dispose: () => { @@ -1429,7 +1434,7 @@ async function activateWithInstalledDistribution( }, }); // Jump-to-definition and find-references - void logger.log("Registering jump-to-definition handlers."); + void extLogger.log("Registering jump-to-definition handlers."); // Store contextual queries in a temporary folder so that they are removed // when the application closes. There is no need for the user to interact with them. @@ -1545,14 +1550,14 @@ async function activateWithInstalledDistribution( await commands.executeCommand("codeQLDatabases.removeOrphanedDatabases"); - void logger.log("Reading query history"); + void extLogger.log("Reading query history"); await qhm.readQueryHistory(); const app = new ExtensionApp(ctx); const dbModule = await initializeDbModule(app); ctx.subscriptions.push(dbModule); - void logger.log("Successfully finished extension initialization."); + void extLogger.log("Successfully finished extension initialization."); return { ctx, @@ -1615,7 +1620,7 @@ function getContextStoragePath(ctx: ExtensionContext) { } async function initializeLogging(ctx: ExtensionContext): Promise { - ctx.subscriptions.push(logger); + ctx.subscriptions.push(extLogger); ctx.subscriptions.push(queryServerLogger); ctx.subscriptions.push(ideServerLogger); } diff --git a/extensions/ql-vscode/src/helpers.ts b/extensions/ql-vscode/src/helpers.ts index 28a82d481..7867f1c03 100644 --- a/extensions/ql-vscode/src/helpers.ts +++ b/extensions/ql-vscode/src/helpers.ts @@ -12,7 +12,7 @@ import { } from "vscode"; import { CodeQLCliServer, QlpacksInfo } from "./cli"; import { UserCancellationException } from "./commandRunner"; -import { logger } from "./common"; +import { extLogger } from "./common"; import { QueryMetadata } from "./pure/interface-types"; // Shared temporary folder for the extension. @@ -29,7 +29,7 @@ export const tmpDirDisposal = { try { tmpDir.removeCallback(); } catch (e) { - void logger.log( + void extLogger.log( `Failed to remove temporary directory ${tmpDir.name}: ${e}`, ); } @@ -51,7 +51,7 @@ export const tmpDirDisposal = { export async function showAndLogErrorMessage( message: string, { - outputLogger = logger, + outputLogger = extLogger, items = [] as string[], fullMessage = undefined as string | undefined, } = {}, @@ -80,7 +80,7 @@ function dropLinesExceptInitial(message: string, n = 2) { */ export async function showAndLogWarningMessage( message: string, - { outputLogger = logger, items = [] as string[] } = {}, + { outputLogger = extLogger, items = [] as string[] } = {}, ): Promise { return internalShowAndLog( message, @@ -100,7 +100,7 @@ export async function showAndLogWarningMessage( */ export async function showAndLogInformationMessage( message: string, - { outputLogger = logger, items = [] as string[], fullMessage = "" } = {}, + { outputLogger = extLogger, items = [] as string[], fullMessage = "" } = {}, ): Promise { return internalShowAndLog( message, @@ -119,7 +119,7 @@ type ShowMessageFn = ( async function internalShowAndLog( message: string, items: string[], - outputLogger = logger, + outputLogger = extLogger, fn: ShowMessageFn, fullMessage?: string, ): Promise { @@ -402,13 +402,13 @@ export async function getQlPackForDbscheme( const packs: QlPackWithPath[] = Object.entries(qlpacks).map( ([packName, dirs]) => { if (dirs.length < 1) { - void logger.log( + void extLogger.log( `In getQlPackFor ${dbschemePath}, qlpack ${packName} has no directories`, ); return { packName, packDir: undefined }; } if (dirs.length > 1) { - void logger.log( + void extLogger.log( `In getQlPackFor ${dbschemePath}, qlpack ${packName} has more than one directory; arbitrarily choosing the first`, ); } @@ -622,10 +622,10 @@ export async function findLanguage( uri, ); const language = Object.keys(queryInfo.byLanguage)[0]; - void logger.log(`Detected query language: ${language}`); + void extLogger.log(`Detected query language: ${language}`); return language; } catch (e) { - void logger.log( + void extLogger.log( "Could not autodetect query language. Select language manually.", ); } @@ -673,7 +673,7 @@ export async function tryGetQueryMetadata( return await cliServer.resolveMetadata(queryPath); } catch (e) { // Ignore errors and provide no metadata. - void logger.log(`Couldn't resolve metadata for ${queryPath}: ${e}`); + void extLogger.log(`Couldn't resolve metadata for ${queryPath}: ${e}`); return; } } diff --git a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts index 96edbf308..32d7a151a 100644 --- a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts +++ b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts @@ -16,7 +16,7 @@ import { } from "../helpers"; import { ProgressCallback } from "../commandRunner"; import { QueryMetadata } from "../pure/interface-types"; -import { logger } from "../common"; +import { extLogger } from "../common"; import * as messages from "../pure/legacy-messages"; import { InitialQueryInfo, LocalQueryInfo } from "../query-results"; import * as qsClient from "./queryserver-client"; @@ -382,7 +382,7 @@ export async function compileAndRunQueryAgainstDatabase( const querySchemaName = path.basename(packConfig.dbscheme); const dbSchemaName = path.basename(dbItem.contents.dbSchemeUri.fsPath); if (querySchemaName != dbSchemaName) { - void logger.log( + void extLogger.log( `Query schema was ${querySchemaName}, but database schema was ${dbSchemaName}.`, ); throw new Error( @@ -411,7 +411,7 @@ export async function compileAndRunQueryAgainstDatabase( let availableMlModels: cli.MlModelInfo[] = []; if (!(await cliServer.cliConstraints.supportsResolveMlModels())) { - void logger.log( + void extLogger.log( "Resolving ML models is unsupported by this version of the CLI. Running the query without any ML models.", ); } else { @@ -423,13 +423,13 @@ export async function compileAndRunQueryAgainstDatabase( ) ).models; if (availableMlModels.length) { - void logger.log( + void extLogger.log( `Found available ML models at the following paths: ${availableMlModels .map((x) => `'${x.path}'`) .join(", ")}.`, ); } else { - void logger.log("Did not find any available ML models."); + void extLogger.log("Did not find any available ML models."); } } catch (e) { const message = @@ -502,7 +502,7 @@ export async function compileAndRunQueryAgainstDatabase( ); if (result.resultType !== messages.QueryResultType.SUCCESS) { const message = result.message || "Failed to run query"; - void logger.log(message); + void extLogger.log(message); void showAndLogErrorMessage(message); } const message = formatLegacyMessage(result); diff --git a/extensions/ql-vscode/src/legacy-query-server/upgrades.ts b/extensions/ql-vscode/src/legacy-query-server/upgrades.ts index f38aaeb77..360a8937a 100644 --- a/extensions/ql-vscode/src/legacy-query-server/upgrades.ts +++ b/extensions/ql-vscode/src/legacy-query-server/upgrades.ts @@ -5,7 +5,7 @@ import { tmpDir, } from "../helpers"; import { ProgressCallback, UserCancellationException } from "../commandRunner"; -import { logger } from "../common"; +import { extLogger } from "../common"; import * as messages from "../pure/legacy-messages"; import * as qsClient from "./queryserver-client"; import * as tmp from "tmp-promise"; @@ -107,7 +107,7 @@ async function checkAndConfirmDatabaseUpgrade( descriptionMessage += `Would perform upgrade: ${script.description}\n`; descriptionMessage += `\t-> Compatibility: ${script.compatibility}\n`; } - void logger.log(descriptionMessage); + void extLogger.log(descriptionMessage); // If the quiet flag is set, do the upgrade without a popup. if (quiet) { @@ -143,7 +143,7 @@ async function checkAndConfirmDatabaseUpgrade( ); if (chosenItem === showLogItem) { - logger.outputChannel.show(); + extLogger.outputChannel.show(); } if (chosenItem !== yesItem) { diff --git a/extensions/ql-vscode/src/log-insights/log-scanner-service.ts b/extensions/ql-vscode/src/log-insights/log-scanner-service.ts index d82492ccb..70a9c55d7 100644 --- a/extensions/ql-vscode/src/log-insights/log-scanner-service.ts +++ b/extensions/ql-vscode/src/log-insights/log-scanner-service.ts @@ -8,7 +8,7 @@ import { } from "./log-scanner"; import { PipelineInfo, SummarySymbols } from "./summary-parser"; import * as fs from "fs-extra"; -import { logger } from "../common"; +import { extLogger } from "../common"; /** * Compute the key used to find a predicate in the summary symbols. @@ -56,7 +56,7 @@ class ProblemReporter implements EvaluationLogProblemReporter { } public log(message: string): void { - void logger.log(message); + void extLogger.log(message); } } diff --git a/extensions/ql-vscode/src/log-insights/summary-language-support.ts b/extensions/ql-vscode/src/log-insights/summary-language-support.ts index 8d4d17f28..6d3011167 100644 --- a/extensions/ql-vscode/src/log-insights/summary-language-support.ts +++ b/extensions/ql-vscode/src/log-insights/summary-language-support.ts @@ -14,7 +14,7 @@ import { } from "vscode"; import { DisposableObject } from "../pure/disposable-object"; import { commandRunner } from "../commandRunner"; -import { logger } from "../common"; +import { extLogger } from "../common"; import { getErrorMessage } from "../pure/helpers-pure"; /** A `Position` within a specified file on disk. */ @@ -103,7 +103,7 @@ export class SummaryLanguageSupport extends DisposableObject { this.sourceMap = await new SourceMapConsumer(rawMap); } catch (e: unknown) { // Error reading sourcemap. Pretend there was no sourcemap. - void logger.log( + void extLogger.log( `Error reading sourcemap file '${mapPath}': ${getErrorMessage(e)}`, ); this.sourceMap = undefined; diff --git a/extensions/ql-vscode/src/packaging.ts b/extensions/ql-vscode/src/packaging.ts index 895d9984d..4bd6e4b11 100644 --- a/extensions/ql-vscode/src/packaging.ts +++ b/extensions/ql-vscode/src/packaging.ts @@ -6,7 +6,7 @@ import { } from "./helpers"; import { QuickPickItem, window } from "vscode"; import { ProgressCallback, UserCancellationException } from "./commandRunner"; -import { logger } from "./common"; +import { extLogger } from "./common"; const QUERY_PACKS = [ "codeql/cpp-queries", @@ -139,7 +139,7 @@ export async function handleInstallPackDependencies( } } if (failedPacks.length > 0) { - void logger.log(`Errors:\n${errors.join("\n")}`); + void extLogger.log(`Errors:\n${errors.join("\n")}`); throw new Error( `Unable to install pack dependencies for: ${failedPacks.join( ", ", diff --git a/extensions/ql-vscode/src/query-history-scrubber.ts b/extensions/ql-vscode/src/query-history-scrubber.ts index f89659082..21dfbc065 100644 --- a/extensions/ql-vscode/src/query-history-scrubber.ts +++ b/extensions/ql-vscode/src/query-history-scrubber.ts @@ -2,7 +2,7 @@ import * as fs from "fs-extra"; import * as os from "os"; import * as path from "path"; import { Disposable, ExtensionContext } from "vscode"; -import { logger } from "./common"; +import { extLogger } from "./common"; import { QueryHistoryManager } from "./query-history"; const LAST_SCRUB_TIME_KEY = "lastScrubTime"; @@ -74,9 +74,9 @@ async function scrubQueries( let scrubCount = 0; // total number of directories deleted try { counter?.increment(); - void logger.log("Scrubbing query directory. Removing old queries."); + void extLogger.log("Scrubbing query directory. Removing old queries."); if (!(await fs.pathExists(queryDirectory))) { - void logger.log( + void extLogger.log( `Cannot scrub. Query directory does not exist: ${queryDirectory}`, ); return; @@ -99,9 +99,9 @@ async function scrubQueries( throw new Error(os.EOL + errors.join(os.EOL)); } } catch (e) { - void logger.log(`Error while scrubbing queries: ${e}`); + void extLogger.log(`Error while scrubbing queries: ${e}`); } finally { - void logger.log(`Scrubbed ${scrubCount} old queries.`); + void extLogger.log(`Scrubbed ${scrubCount} old queries.`); } await qhm.removeDeletedQueries(); } @@ -119,30 +119,30 @@ async function scrubDirectory( try { let deleted = true; if (!(await fs.stat(dir)).isDirectory()) { - void logger.log(` ${dir} is not a directory. Deleting.`); + void extLogger.log(` ${dir} is not a directory. Deleting.`); await fs.remove(dir); } else if (!(await fs.pathExists(timestampFile))) { - void logger.log(` ${dir} has no timestamp file. Deleting.`); + void extLogger.log(` ${dir} has no timestamp file. Deleting.`); await fs.remove(dir); } else if (!(await fs.stat(timestampFile)).isFile()) { - void logger.log(` ${timestampFile} is not a file. Deleting.`); + void extLogger.log(` ${timestampFile} is not a file. Deleting.`); await fs.remove(dir); } else { const timestampText = await fs.readFile(timestampFile, "utf8"); const timestamp = parseInt(timestampText, 10); if (Number.isNaN(timestamp)) { - void logger.log( + void extLogger.log( ` ${dir} has invalid timestamp '${timestampText}'. Deleting.`, ); await fs.remove(dir); } else if (now - timestamp > maxQueryTime) { - void logger.log( + void extLogger.log( ` ${dir} is older than ${maxQueryTime / 1000} seconds. Deleting.`, ); await fs.remove(dir); } else { - void logger.log( + void extLogger.log( ` ${dir} is not older than ${maxQueryTime / 1000} seconds. Keeping.`, ); deleted = false; diff --git a/extensions/ql-vscode/src/query-history.ts b/extensions/ql-vscode/src/query-history.ts index 3a63cade5..5ddbc4e28 100644 --- a/extensions/ql-vscode/src/query-history.ts +++ b/extensions/ql-vscode/src/query-history.ts @@ -24,7 +24,7 @@ import { showAndLogWarningMessage, showBinaryChoiceDialog, } from "./helpers"; -import { logger } from "./common"; +import { extLogger } from "./common"; import { URLSearchParams } from "url"; import { DisposableObject } from "./pure/disposable-object"; import { commandRunner } from "./commandRunner"; @@ -460,7 +460,7 @@ export class QueryHistoryManager extends DisposableObject { }), ); - void logger.log("Registering query history panel commands."); + void extLogger.log("Registering query history panel commands."); this.push( commandRunner( "codeQLQueryHistory.openQuery", @@ -705,7 +705,7 @@ export class QueryHistoryManager extends DisposableObject { }); await this.refreshTreeView(); } else { - void logger.log( + void extLogger.log( "Variant analysis status update event received for unknown variant analysis", ); } @@ -775,7 +775,7 @@ export class QueryHistoryManager extends DisposableObject { } await this.refreshTreeView(); } else { - void logger.log( + void extLogger.log( "Variant analysis status update event received for unknown variant analysis", ); } @@ -787,7 +787,7 @@ export class QueryHistoryManager extends DisposableObject { } async readQueryHistory(): Promise { - void logger.log( + void extLogger.log( `Reading cached query history from '${this.queryMetadataStorageLocation}'.`, ); const history = await slurpQueryHistory(this.queryMetadataStorageLocation); @@ -929,9 +929,9 @@ export class QueryHistoryManager extends DisposableObject { // Remote queries can be removed locally, but not remotely. // The user must cancel the query on GitHub Actions explicitly. this.treeDataProvider.remove(item); - void logger.log(`Deleted ${this.labelProvider.getLabel(item)}.`); + void extLogger.log(`Deleted ${this.labelProvider.getLabel(item)}.`); if (item.status === QueryStatus.InProgress) { - void logger.log( + void extLogger.log( "The variant analysis is still running on GitHub Actions. To cancel there, you must go to the workflow run in your browser.", ); } @@ -945,9 +945,9 @@ export class QueryHistoryManager extends DisposableObject { // We can remove a Variant Analysis locally, but not remotely. // The user must cancel the query on GitHub Actions explicitly. this.treeDataProvider.remove(item); - void logger.log(`Deleted ${this.labelProvider.getLabel(item)}.`); + void extLogger.log(`Deleted ${this.labelProvider.getLabel(item)}.`); if (item.status === QueryStatus.InProgress) { - void logger.log( + void extLogger.log( "The variant analysis is still running on GitHub Actions. To cancel there, you must go to the workflow run in your browser.", ); } @@ -1604,8 +1604,8 @@ the file in the file explorer and dragging it into the workspace.`, } } else { void showAndLogErrorMessage(`Could not open file ${fileLocation}`); - void logger.log(getErrorMessage(e)); - void logger.log(getErrorStack(e)); + void extLogger.log(getErrorMessage(e)); + void extLogger.log(getErrorStack(e)); } } } diff --git a/extensions/ql-vscode/src/query-server/run-queries.ts b/extensions/ql-vscode/src/query-server/run-queries.ts index 99f1be795..2b54e688a 100644 --- a/extensions/ql-vscode/src/query-server/run-queries.ts +++ b/extensions/ql-vscode/src/query-server/run-queries.ts @@ -9,7 +9,7 @@ import { showAndLogWarningMessage, tryGetQueryMetadata, } from "../helpers"; -import { logger } from "../common"; +import { extLogger } from "../common"; import * as messages from "../pure/new-messages"; import * as legacyMessages from "../pure/legacy-messages"; import { InitialQueryInfo, LocalQueryInfo } from "../query-results"; @@ -110,7 +110,7 @@ export async function compileAndRunQueryAgainstDatabase( if (result.resultType !== messages.QueryResultType.SUCCESS) { const message = result.message || "Failed to run query"; - void logger.log(message); + void extLogger.log(message); void showAndLogErrorMessage(message); } let message; diff --git a/extensions/ql-vscode/src/remote-queries/export-results.ts b/extensions/ql-vscode/src/remote-queries/export-results.ts index bdb83dfbe..33c5de803 100644 --- a/extensions/ql-vscode/src/remote-queries/export-results.ts +++ b/extensions/ql-vscode/src/remote-queries/export-results.ts @@ -12,7 +12,7 @@ import { import { Credentials } from "../authentication"; import { UserCancellationException } from "../commandRunner"; import { showInformationMessageWithAction } from "../helpers"; -import { logger } from "../common"; +import { extLogger } from "../common"; import { QueryHistoryManager } from "../query-history"; import { createGist } from "./gh-api/gh-api-client"; import { RemoteQueriesManager } from "./remote-queries-manager"; @@ -76,7 +76,7 @@ export async function exportRemoteQueryResults( ): Promise { const queryHistoryItem = queryHistoryManager.getRemoteQueryById(queryId); if (!queryHistoryItem) { - void logger.log(`Could not find query with id ${queryId}`); + void extLogger.log(`Could not find query with id ${queryId}`); throw new Error( "There was an error when trying to retrieve variant analysis information", ); @@ -86,7 +86,7 @@ export async function exportRemoteQueryResults( throw new Error("Variant analysis results are not yet available."); } - void logger.log( + void extLogger.log( `Exporting variant analysis results for query: ${queryHistoryItem.queryId}`, ); const query = queryHistoryItem.remoteQuery; @@ -148,7 +148,7 @@ export async function exportVariantAnalysisResults( variantAnalysisId, ); if (!variantAnalysis) { - void logger.log( + void extLogger.log( `Could not find variant analysis with id ${variantAnalysisId}`, ); throw new Error( @@ -156,7 +156,7 @@ export async function exportVariantAnalysisResults( ); } - void logger.log( + void extLogger.log( `Exporting variant analysis results for variant analysis with id ${variantAnalysis.id}`, ); diff --git a/extensions/ql-vscode/src/remote-queries/gh-api/gh-actions-api-client.ts b/extensions/ql-vscode/src/remote-queries/gh-api/gh-actions-api-client.ts index 80b9ac320..7a3485b28 100644 --- a/extensions/ql-vscode/src/remote-queries/gh-api/gh-actions-api-client.ts +++ b/extensions/ql-vscode/src/remote-queries/gh-api/gh-actions-api-client.ts @@ -6,7 +6,7 @@ import { tmpDir, } from "../../helpers"; import { Credentials } from "../../authentication"; -import { logger } from "../../common"; +import { extLogger } from "../../common"; import { RemoteQueryWorkflowResult } from "../remote-query-workflow-result"; import { DownloadLink, createDownloadPath } from "../download-link"; import { RemoteQuery } from "../remote-query"; @@ -384,10 +384,10 @@ async function unzipBuffer( filePath: string, destinationPath: string, ): Promise { - void logger.log(`Saving file to ${filePath}`); + void extLogger.log(`Saving file to ${filePath}`); await fs.writeFile(filePath, Buffer.from(data)); - void logger.log(`Unzipping file to ${destinationPath}`); + void extLogger.log(`Unzipping file to ${destinationPath}`); await unzipFile(filePath, destinationPath); } diff --git a/extensions/ql-vscode/src/remote-queries/repository-selection.ts b/extensions/ql-vscode/src/remote-queries/repository-selection.ts index 4551ed55c..ccfe92109 100644 --- a/extensions/ql-vscode/src/remote-queries/repository-selection.ts +++ b/extensions/ql-vscode/src/remote-queries/repository-selection.ts @@ -1,6 +1,6 @@ import * as fs from "fs-extra"; import { QuickPickItem, window } from "vscode"; -import { logger } from "../common"; +import { extLogger } from "../common"; import { getRemoteRepositoryLists, getRemoteRepositoryListsPath, @@ -50,12 +50,12 @@ export async function getRepositorySelection(): Promise { ); if (quickpick?.repositories?.length) { - void logger.log( + void extLogger.log( `Selected repositories: ${quickpick.repositories.join(", ")}`, ); return { repositories: quickpick.repositories }; } else if (quickpick?.repositoryList) { - void logger.log(`Selected repository list: ${quickpick.repositoryList}`); + void extLogger.log(`Selected repository list: ${quickpick.repositoryList}`); return { repositoryLists: [quickpick.repositoryList] }; } else if (quickpick?.useCustomRepo) { const customRepo = await getCustomRepo(); @@ -68,7 +68,7 @@ export async function getRepositorySelection(): Promise { "Invalid repository format. Please enter a valid repository in the format / (e.g. github/codeql)", ); } - void logger.log(`Entered repository: ${customRepo}`); + void extLogger.log(`Entered repository: ${customRepo}`); return { repositories: [customRepo] }; } else if (quickpick?.useAllReposOfOwner) { const owner = await getOwner(); @@ -79,7 +79,7 @@ export async function getRepositorySelection(): Promise { if (!owner || !OWNER_REGEX.test(owner)) { throw new Error(`Invalid user or organization: ${owner}`); } - void logger.log(`Entered owner: ${owner}`); + void extLogger.log(`Entered owner: ${owner}`); return { owners: [owner] }; } else { // We don't need to display a warning pop-up in this case, since the user just escaped out of the operation. diff --git a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts b/extensions/ql-vscode/src/remote-queries/run-remote-query.ts index 6b85fac07..28ec9f023 100644 --- a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts +++ b/extensions/ql-vscode/src/remote-queries/run-remote-query.ts @@ -12,7 +12,7 @@ import { } from "../helpers"; import { Credentials } from "../authentication"; import * as cli from "../cli"; -import { logger } from "../common"; +import { extLogger } from "../common"; import { getActionBranch, getRemoteControllerRepo, @@ -97,7 +97,7 @@ async function generateQueryPack( }), }); - void logger.log(`Copied ${copiedCount} files to ${queryPackDir}`); + void extLogger.log(`Copied ${copiedCount} files to ${queryPackDir}`); await fixPackFile(queryPackDir, packRelativePath); @@ -108,9 +108,9 @@ async function generateQueryPack( // copy only the query file to the query pack directory // and generate a synthetic query pack - void logger.log(`Copying ${queryFile} to ${queryPackDir}`); + void extLogger.log(`Copying ${queryFile} to ${queryPackDir}`); await fs.copy(queryFile, targetQueryFileName); - void logger.log("Generating synthetic query pack"); + void extLogger.log("Generating synthetic query pack"); const syntheticQueryPack = { name: QUERY_PACK_NAME, version: "0.0.0", @@ -144,7 +144,7 @@ async function generateQueryPack( } const bundlePath = await getPackedBundlePath(queryPackDir); - void logger.log( + void extLogger.log( `Compiling and bundling query pack from ${queryPackDir} to ${bundlePath}. (This may take a while.)`, ); await cliServer.packInstall(queryPackDir); @@ -359,7 +359,7 @@ export async function getControllerRepo( let controllerRepoNwo: string | undefined; controllerRepoNwo = getRemoteControllerRepo(); if (!controllerRepoNwo || !REPO_REGEX.test(controllerRepoNwo)) { - void logger.log( + void extLogger.log( controllerRepoNwo ? "Invalid controller repository name." : "No controller repository defined.", @@ -380,13 +380,13 @@ export async function getControllerRepo( "Invalid repository format. Must be a valid GitHub repository in the format /.", ); } - void logger.log( + void extLogger.log( `Setting the controller repository as: ${controllerRepoNwo}`, ); await setRemoteControllerRepo(controllerRepoNwo); } - void logger.log(`Using controller repository: ${controllerRepoNwo}`); + void extLogger.log(`Using controller repository: ${controllerRepoNwo}`); const [owner, repo] = controllerRepoNwo.split("/"); try { @@ -395,7 +395,7 @@ export async function getControllerRepo( owner, repo, ); - void logger.log(`Controller repository ID: ${controllerRepo.id}`); + void extLogger.log(`Controller repository ID: ${controllerRepo.id}`); return { id: controllerRepo.id, fullName: controllerRepo.full_name, diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts b/extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts index 1cad53299..da53201cc 100644 --- a/extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts +++ b/extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts @@ -8,7 +8,7 @@ import { } from "vscode"; import { URLSearchParams } from "url"; import { AbstractWebview, WebviewPanelConfig } from "../abstract-webview"; -import { logger } from "../common"; +import { extLogger } from "../common"; import { FromVariantAnalysisMessage, ToVariantAnalysisMessage, @@ -159,7 +159,7 @@ export class VariantAnalysisView protected async onWebViewLoaded() { super.onWebViewLoaded(); - void logger.log("Variant analysis view loaded"); + void extLogger.log("Variant analysis view loaded"); const variantAnalysis = await this.manager.getVariantAnalysis( this.variantAnalysisId, diff --git a/extensions/ql-vscode/src/run-queries-shared.ts b/extensions/ql-vscode/src/run-queries-shared.ts index c81449565..bed2ac301 100644 --- a/extensions/ql-vscode/src/run-queries-shared.ts +++ b/extensions/ql-vscode/src/run-queries-shared.ts @@ -25,7 +25,7 @@ import { CodeQLCliServer } from "./cli"; import { SELECT_QUERY_NAME } from "./contextual/locationFinder"; import { DatabaseManager } from "./databases"; import { DecodedBqrsChunk } from "./pure/bqrs-cli-types"; -import { logger } from "./common"; +import { extLogger } from "./common"; import { Logger } from "./common"; import { generateSummarySymbolsFile } from "./log-insights/summary-parser"; import { asError } from "./pure/helpers-pure"; @@ -143,7 +143,7 @@ export class QueryEvaluationInfo { */ canHaveInterpretedResults(): boolean { if (!this.databaseHasMetadataFile) { - void logger.log( + void extLogger.log( "Cannot produce interpreted results since the database does not have a .dbinfo or codeql-database.yml file.", ); return false; @@ -152,7 +152,7 @@ export class QueryEvaluationInfo { const kind = this.metadata?.kind; const hasKind = !!kind; if (!hasKind) { - void logger.log( + void extLogger.log( "Cannot produce interpreted results since the query does not have @kind metadata.", ); return false; diff --git a/extensions/ql-vscode/src/telemetry.ts b/extensions/ql-vscode/src/telemetry.ts index 54bdab469..0b86eb15a 100644 --- a/extensions/ql-vscode/src/telemetry.ts +++ b/extensions/ql-vscode/src/telemetry.ts @@ -14,7 +14,7 @@ import { isIntegrationTestMode, } from "./config"; import * as appInsights from "applicationinsights"; -import { logger } from "./common"; +import { extLogger } from "./common"; import { UserCancellationException } from "./commandRunner"; import { showBinaryChoiceWithUrlDialog } from "./helpers"; @@ -135,7 +135,7 @@ export class TelemetryListener extends ConfigListener { } if (LOG_TELEMETRY.getValue()) { - void logger.log(`Telemetry: ${JSON.stringify(envelope)}`); + void extLogger.log(`Telemetry: ${JSON.stringify(envelope)}`); } return true; }); diff --git a/extensions/ql-vscode/src/test-ui.ts b/extensions/ql-vscode/src/test-ui.ts index c0bcc99c4..3a1b933b7 100644 --- a/extensions/ql-vscode/src/test-ui.ts +++ b/extensions/ql-vscode/src/test-ui.ts @@ -16,7 +16,7 @@ import { TestTreeNode } from "./test-tree-node"; import { DisposableObject } from "./pure/disposable-object"; import { UIService } from "./vscode-utils/ui-service"; import { QLTestAdapter, getExpectedFile, getActualFile } from "./test-adapter"; -import { logger } from "./common"; +import { extLogger } from "./common"; type VSCodeTestEvent = | TestRunStartedEvent @@ -48,7 +48,7 @@ export class TestUIService extends UIService implements TestController { constructor(private readonly testHub: TestHub) { super(); - void logger.log("Registering CodeQL test panel commands."); + void extLogger.log("Registering CodeQL test panel commands."); this.registerCommand( "codeQLTests.showOutputDifferences", this.showOutputDifferences, diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts index 35e257d38..8f0b74c41 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/legacy-query.test.ts @@ -11,7 +11,7 @@ import { extensions } from "vscode"; import { CodeQLExtensionInterface } from "../../extension"; import { describeWithCodeQL } from "../cli"; import { QueryServerClient } from "../../legacy-query-server/queryserver-client"; -import { logger, ProgressReporter } from "../../common"; +import { extLogger, ProgressReporter } from "../../common"; const baseDir = path.join(__dirname, "../../../test/data"); @@ -134,7 +134,7 @@ describeWithCodeQL()("using the legacy query server", () => { cliServer, { contextStoragePath: tmpDir.name, - logger, + logger: extLogger, }, (task) => task(nullProgressReporter, new CancellationTokenSource().token), diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts index 456bce7b6..b1682d80e 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/new-query.test.ts @@ -9,7 +9,7 @@ import { extensions, Uri } from "vscode"; import { CodeQLExtensionInterface } from "../../extension"; import { describeWithCodeQL } from "../cli"; import { QueryServerClient } from "../../query-server/queryserver-client"; -import { logger, ProgressReporter } from "../../common"; +import { extLogger, ProgressReporter } from "../../common"; import { QueryResultType } from "../../pure/new-messages"; import { cleanDatabases, dbLoc, storagePath } from "./global.helper"; import { importArchiveDatabase } from "../../databaseFetcher"; @@ -135,7 +135,7 @@ describeWithCodeQL()("using the new query server", () => { cliServer, { contextStoragePath: tmpDir.name, - logger, + logger: extLogger, }, (task) => task(nullProgressReporter, new CancellationTokenSource().token), diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts index 37d979ddb..71c52fa8e 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts @@ -8,7 +8,7 @@ import { window, } from "vscode"; import { CodeQLExtensionInterface } from "../../../extension"; -import { logger } from "../../../common"; +import { extLogger } from "../../../common"; import * as config from "../../../config"; import { setRemoteControllerRepo, @@ -75,7 +75,7 @@ describe("Variant Analysis Manager", () => { outputJsonStub = jest.spyOn(fs, "outputJson").mockReturnValue(undefined); writeFileStub = jest.spyOn(fs, "writeFile").mockReturnValue(undefined); - jest.spyOn(logger, "log").mockResolvedValue(undefined); + jest.spyOn(extLogger, "log").mockResolvedValue(undefined); jest .spyOn(config, "isVariantAnalysisLiveResultsEnabled") .mockReturnValue(false); @@ -97,7 +97,7 @@ describe("Variant Analysis Manager", () => { cli = extension.cliServer; variantAnalysisResultsManager = new VariantAnalysisResultsManager( cli, - logger, + extLogger, ); variantAnalysisManager = new VariantAnalysisManager( extension.ctx, diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts index f77229f65..c122dd0d5 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts @@ -1,6 +1,6 @@ import { extensions } from "vscode"; import { CodeQLExtensionInterface } from "../../../extension"; -import { logger } from "../../../common"; +import { extLogger } from "../../../common"; import { Credentials } from "../../../authentication"; import * as fs from "fs-extra"; import * as path from "path"; @@ -21,7 +21,7 @@ describe(VariantAnalysisResultsManager.name, () => { let variantAnalysisResultsManager: VariantAnalysisResultsManager; beforeEach(async () => { - jest.spyOn(logger, "log").mockResolvedValue(undefined); + jest.spyOn(extLogger, "log").mockResolvedValue(undefined); jest.spyOn(fs, "mkdirSync").mockReturnValue(undefined); jest.spyOn(fs, "writeFile").mockReturnValue(undefined); @@ -35,7 +35,7 @@ describe(VariantAnalysisResultsManager.name, () => { cli = extension.cliServer; variantAnalysisResultsManager = new VariantAnalysisResultsManager( cli, - logger, + extLogger, ); }); diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts index 25cc3a42c..2df5cfeae 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts @@ -3,7 +3,7 @@ import * as fetch from "node-fetch"; import * as semver from "semver"; import * as helpers from "../../helpers"; -import { logger } from "../../common"; +import { extLogger } from "../../common"; import * as fs from "fs-extra"; import * as os from "os"; import { @@ -199,7 +199,7 @@ describe("Launcher path", () => { let warnSpy: jest.SpiedFunction; let errorSpy: jest.SpiedFunction; - let logSpy: jest.SpiedFunction; + let logSpy: jest.SpiedFunction; let pathExistsSpy: jest.SpiedFunction; let launcherThatExists = ""; @@ -211,7 +211,7 @@ describe("Launcher path", () => { errorSpy = jest .spyOn(helpers, "showAndLogErrorMessage") .mockResolvedValue(undefined); - logSpy = jest.spyOn(logger, "log").mockResolvedValue(undefined); + logSpy = jest.spyOn(extLogger, "log").mockResolvedValue(undefined); pathExistsSpy = jest .spyOn(fs, "pathExists") .mockImplementation(async (path: string) => { diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts index 48f010b14..7b31e0096 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts @@ -2,7 +2,7 @@ import * as fs from "fs-extra"; import * as path from "path"; import * as vscode from "vscode"; -import { logger } from "../../common"; +import { extLogger } from "../../common"; import { registerQueryHistoryScrubber } from "../../query-history-scrubber"; import { HistoryTreeDataProvider, @@ -88,7 +88,7 @@ describe("query-history", () => { .spyOn(vscode.commands, "executeCommand") .mockResolvedValue(undefined); - jest.spyOn(logger, "log").mockResolvedValue(undefined); + jest.spyOn(extLogger, "log").mockResolvedValue(undefined); tryOpenExternalFile = (QueryHistoryManager.prototype as any) .tryOpenExternalFile; From 3751f3ec6c6250fd34c3185dc69e5af9dd0c602b Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 30 Nov 2022 15:28:21 +0000 Subject: [PATCH 56/58] Add tests --- .../VariantAnalysisStatusStats.tsx | 4 +- .../VariantAnalysisStatusStats.spec.tsx | 74 +++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx index 675a49bc1..52c41ae17 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx @@ -4,7 +4,7 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"; import { formatDate } from "../../pure/date"; import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis"; -type Props = { +export type VariantAnalysisStatusStatsProps = { variantAnalysisStatus: VariantAnalysisStatus; completedAt?: Date; @@ -26,7 +26,7 @@ export const VariantAnalysisStatusStats = ({ variantAnalysisStatus, completedAt, onViewLogsClick, -}: Props) => { +}: VariantAnalysisStatusStatsProps) => { if (variantAnalysisStatus === VariantAnalysisStatus.InProgress) { return ; } diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx new file mode 100644 index 000000000..c84261615 --- /dev/null +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx @@ -0,0 +1,74 @@ +import * as React from "react"; +import { render as reactRender, screen } from "@testing-library/react"; +import { VariantAnalysisStatus } from "../../../remote-queries/shared/variant-analysis"; +import { + VariantAnalysisStatusStats, + VariantAnalysisStatusStatsProps, +} from "../VariantAnalysisStatusStats"; +import { formatDate } from "../../../pure/date"; + +describe(VariantAnalysisStatusStats.name, () => { + const onViewLogsClick = jest.fn(); + + afterEach(() => { + onViewLogsClick.mockReset(); + }); + + const render = (props: Partial = {}) => + reactRender( + , + ); + + it("renders an in-progress status correctly", () => { + const { container } = render({ + variantAnalysisStatus: VariantAnalysisStatus.InProgress, + }); + + expect( + container.getElementsByClassName( + "codicon codicon-loading codicon-modifier-spin", + ).length, + ).toEqual(1); + }); + + it("renders when there is a completedAt date", () => { + const completedAt = new Date(); + render({ + variantAnalysisStatus: VariantAnalysisStatus.Succeeded, + completedAt, + }); + + expect(screen.getByText(formatDate(completedAt))).toBeInTheDocument(); + expect(screen.queryByText("-")).not.toBeInTheDocument(); + }); + + it("renders when there isn't a completedAt date", () => { + render({ + variantAnalysisStatus: VariantAnalysisStatus.Succeeded, + completedAt: undefined, + }); + + expect(screen.getByText("-")).toBeInTheDocument(); + }); + + it("renders when there is a viewLogs links", () => { + render({ + variantAnalysisStatus: VariantAnalysisStatus.Succeeded, + onViewLogsClick: () => undefined, + }); + + expect(screen.getByText("View logs")).toBeInTheDocument(); + }); + + it("renders when there isn't a viewLogs links", () => { + render({ + variantAnalysisStatus: VariantAnalysisStatus.Succeeded, + onViewLogsClick: undefined, + }); + + expect(screen.queryByText("View logs")).not.toBeInTheDocument(); + }); +}); From c958fd20522a68d5df33866ad2da4b768b116287 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 30 Nov 2022 16:40:46 +0000 Subject: [PATCH 57/58] Remove unused mock --- .../__tests__/VariantAnalysisStatusStats.spec.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx index c84261615..a2cee8a75 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx @@ -8,12 +8,6 @@ import { import { formatDate } from "../../../pure/date"; describe(VariantAnalysisStatusStats.name, () => { - const onViewLogsClick = jest.fn(); - - afterEach(() => { - onViewLogsClick.mockReset(); - }); - const render = (props: Partial = {}) => reactRender( Date: Wed, 30 Nov 2022 16:45:36 +0000 Subject: [PATCH 58/58] Use toBeInTheDocument --- .../__tests__/VariantAnalysisStatusStats.spec.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx index a2cee8a75..fd356bc5f 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStatusStats.spec.tsx @@ -24,8 +24,8 @@ describe(VariantAnalysisStatusStats.name, () => { expect( container.getElementsByClassName( "codicon codicon-loading codicon-modifier-spin", - ).length, - ).toEqual(1); + )[0], + ).toBeInTheDocument(); }); it("renders when there is a completedAt date", () => {