Merge pull request #1166 from github/aeisenberg/remote-queries-unit-tests
Add unit tests for query history and remote queries
This commit is contained in:
42
extensions/ql-vscode/package-lock.json
generated
42
extensions/ql-vscode/package-lock.json
generated
@@ -55,7 +55,7 @@
|
||||
"@types/jszip": "~3.1.6",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/nanoid": "^3.0.0",
|
||||
"@types/node": "^12.14.1",
|
||||
"@types/node": "^16.11.25",
|
||||
"@types/node-fetch": "~2.5.2",
|
||||
"@types/proxyquire": "~1.3.28",
|
||||
"@types/react": "^17.0.2",
|
||||
@@ -1002,11 +1002,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/glob/node_modules/@types/node": {
|
||||
"version": "14.0.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.23.tgz",
|
||||
"integrity": "sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw=="
|
||||
},
|
||||
"node_modules/@types/google-protobuf": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.7.2.tgz",
|
||||
@@ -1220,10 +1215,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "12.19.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.4.tgz",
|
||||
"integrity": "sha512-o3oj1bETk8kBwzz1WlO6JWL/AfAA3Vm6J1B3C9CsdxHYp7XgPiH7OEXPUbZTndHlRaIElrANkQfe6ZmfJb3H2w==",
|
||||
"dev": true
|
||||
"version": "16.11.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.25.tgz",
|
||||
"integrity": "sha512-NrTwfD7L1RTc2qrHQD4RTTy4p0CO2LatKBEKEds3CaVuhoM/+DJzmWZl5f+ikR8cm8F5mfJxK+9rQq07gRiSjQ=="
|
||||
},
|
||||
"node_modules/@types/node-fetch": {
|
||||
"version": "2.5.7",
|
||||
@@ -1291,12 +1285,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/semver/node_modules/@types/node": {
|
||||
"version": "14.0.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.23.tgz",
|
||||
"integrity": "sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/sinon": {
|
||||
"version": "7.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.2.tgz",
|
||||
@@ -13912,13 +13900,6 @@
|
||||
"requires": {
|
||||
"@types/minimatch": "*",
|
||||
"@types/node": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "14.0.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.23.tgz",
|
||||
"integrity": "sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/glob-stream": {
|
||||
@@ -14109,10 +14090,9 @@
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.19.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.4.tgz",
|
||||
"integrity": "sha512-o3oj1bETk8kBwzz1WlO6JWL/AfAA3Vm6J1B3C9CsdxHYp7XgPiH7OEXPUbZTndHlRaIElrANkQfe6ZmfJb3H2w==",
|
||||
"dev": true
|
||||
"version": "16.11.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.25.tgz",
|
||||
"integrity": "sha512-NrTwfD7L1RTc2qrHQD4RTTy4p0CO2LatKBEKEds3CaVuhoM/+DJzmWZl5f+ikR8cm8F5mfJxK+9rQq07gRiSjQ=="
|
||||
},
|
||||
"@types/node-fetch": {
|
||||
"version": "2.5.7",
|
||||
@@ -14178,14 +14158,6 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "14.0.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.23.tgz",
|
||||
"integrity": "sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/sinon": {
|
||||
|
||||
@@ -1064,7 +1064,7 @@
|
||||
"@types/jszip": "~3.1.6",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/nanoid": "^3.0.0",
|
||||
"@types/node": "^12.14.1",
|
||||
"@types/node": "^16.11.25",
|
||||
"@types/node-fetch": "~2.5.2",
|
||||
"@types/proxyquire": "~1.3.28",
|
||||
"@types/react": "^17.0.2",
|
||||
|
||||
11
extensions/ql-vscode/src/blob.d.ts
vendored
11
extensions/ql-vscode/src/blob.d.ts
vendored
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* The npm library jszip is designed to work in both the browser and
|
||||
* node. Consequently its typings @types/jszip refers to both node
|
||||
* types like `Buffer` (which don't exist in the browser), and browser
|
||||
* types like `Blob` (which don't exist in node). Instead of sticking
|
||||
* all of `dom` in `compilerOptions.lib`, it suffices just to put in a
|
||||
* stub definition of the type `Blob` here so that compilation
|
||||
* succeeds.
|
||||
*/
|
||||
|
||||
declare type Blob = string;
|
||||
@@ -403,7 +403,7 @@ export class CodeQLCliServer implements Disposable {
|
||||
try {
|
||||
if (cancellationToken !== undefined) {
|
||||
cancellationRegistration = cancellationToken.onCancellationRequested(_e => {
|
||||
tk(child.pid);
|
||||
tk(child.pid || 0);
|
||||
});
|
||||
}
|
||||
if (logger !== undefined) {
|
||||
|
||||
@@ -574,7 +574,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
const current = this.treeDataProvider.getCurrent();
|
||||
if (current !== undefined) {
|
||||
await this.treeView.reveal(current, { select: true });
|
||||
await this._onWillOpenQueryItem.fire(current);
|
||||
this._onWillOpenQueryItem.fire(current);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,7 +655,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
|
||||
async handleItemClicked(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[]
|
||||
multiSelect: QueryHistoryInfo[] = []
|
||||
) {
|
||||
const { finalSingleItem, finalMultiSelect } = this.determineSelection(singleItem, multiSelect);
|
||||
if (!this.assertSingleQuery(finalMultiSelect) || !finalSingleItem) {
|
||||
@@ -676,8 +676,10 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
// show original query file on double click
|
||||
await this.handleOpenQuery(finalSingleItem, [finalSingleItem]);
|
||||
} else {
|
||||
// show results on single click
|
||||
await this._onWillOpenQueryItem.fire(finalSingleItem);
|
||||
// show results on single click only if query is completed successfully.
|
||||
if (finalSingleItem.status === QueryStatus.Completed) {
|
||||
await this._onWillOpenQueryItem.fire(finalSingleItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -718,7 +720,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
|
||||
async handleShowQueryText(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[]
|
||||
multiSelect: QueryHistoryInfo[] = []
|
||||
) {
|
||||
const { finalSingleItem, finalMultiSelect } = this.determineSelection(singleItem, multiSelect);
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ export class QueryServerClient extends DisposableObject {
|
||||
}
|
||||
|
||||
get serverProcessPid(): number {
|
||||
return this.serverProcess!.child.pid;
|
||||
return this.serverProcess!.child.pid || 0;
|
||||
}
|
||||
|
||||
async sendRequest<P, R, E, RO>(type: RequestType<WithProgressId<P>, R, E, RO>, parameter: P, token?: CancellationToken, progress?: (res: ProgressMessage) => void): Promise<R> {
|
||||
|
||||
@@ -106,7 +106,8 @@ export class AnalysesResultsManager {
|
||||
const resultsForQuery = this.internalGetAnalysesResults(queryId);
|
||||
resultsForQuery.push(analysisResults);
|
||||
this.analysesResults.set(queryId, resultsForQuery);
|
||||
void publishResults(resultsForQuery);
|
||||
void publishResults([...resultsForQuery]);
|
||||
const pos = resultsForQuery.length - 1;
|
||||
|
||||
let artifactPath;
|
||||
try {
|
||||
@@ -116,16 +117,23 @@ export class AnalysesResultsManager {
|
||||
throw new Error(`Could not download the analysis results for ${analysis.nwo}: ${e.message}`);
|
||||
}
|
||||
|
||||
let newAnaysisResults: AnalysisResults;
|
||||
if (path.extname(artifactPath) === '.sarif') {
|
||||
const queryResults = await this.readResults(artifactPath);
|
||||
analysisResults.results = queryResults;
|
||||
analysisResults.status = 'Completed';
|
||||
newAnaysisResults = {
|
||||
...analysisResults,
|
||||
results: queryResults,
|
||||
status: 'Completed'
|
||||
};
|
||||
} else {
|
||||
void this.logger.log('Cannot download results. Only alert and path queries are fully supported.');
|
||||
analysisResults.status = 'Failed';
|
||||
newAnaysisResults = {
|
||||
...analysisResults,
|
||||
status: 'Failed'
|
||||
};
|
||||
}
|
||||
|
||||
void publishResults(resultsForQuery);
|
||||
resultsForQuery[pos] = newAnaysisResults;
|
||||
void publishResults([...resultsForQuery]);
|
||||
}
|
||||
|
||||
private async readResults(filePath: string): Promise<QueryResult[]> {
|
||||
|
||||
23
extensions/ql-vscode/src/vscode-tests/directory-walker.ts
Normal file
23
extensions/ql-vscode/src/vscode-tests/directory-walker.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs-extra';
|
||||
|
||||
/**
|
||||
* Recursively walk a directory and return the full path to all files found.
|
||||
* Note that this function uses synchronous fs calls, so it should only be used in tests.
|
||||
*
|
||||
* @param dir the directory to walk
|
||||
*
|
||||
* @return An iterator of the full path to all files recursively found in the directory.
|
||||
*/
|
||||
export function* walk(dir: string): IterableIterator<string> {
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const file of files) {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
if (stat.isDirectory()) {
|
||||
yield* walk(filePath);
|
||||
} else {
|
||||
yield filePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
extensions/ql-vscode/src/vscode-tests/disposable-bucket.ts
Normal file
16
extensions/ql-vscode/src/vscode-tests/disposable-bucket.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Disposable } from 'vscode';
|
||||
import { DisposableObject } from '../pure/disposable-object';
|
||||
|
||||
/**
|
||||
* A simple disposable object that does nothing other than contain a list of disposable objects.
|
||||
* This is useful for implementing a `Disposable` that owns other disposable objects.
|
||||
*/
|
||||
export class DisposableBucket extends DisposableObject {
|
||||
/**
|
||||
* Add a disposable object to this bucket.
|
||||
* @param obj The object to add.
|
||||
*/
|
||||
public push<T extends Disposable>(obj: T): T {
|
||||
return super.push(obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @name MRVA Integration test 1
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id javascript/integration-test-1
|
||||
*/
|
||||
import javascript
|
||||
|
||||
from MemberDeclaration md
|
||||
where md.getName() = "dispose"
|
||||
select md, "Dispose method"
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @name MRVA Integration test 2
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id javascript/integration-test-2
|
||||
*/
|
||||
import javascript
|
||||
|
||||
from MemberDeclaration md
|
||||
where md.getName() = "refresh"
|
||||
select md, "Refresh method"
|
||||
@@ -0,0 +1 @@
|
||||
other/hucairz
|
||||
@@ -0,0 +1 @@
|
||||
15
|
||||
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
"md","col1"
|
||||
"dispose ... ();\n }","Dispose method"
|
||||
"readonl ... > void;","Dispose method"
|
||||
"async d ... }\n }","Dispose method"
|
||||
"dispose(): any;","Dispose method"
|
||||
"public ... }\n }","Dispose method"
|
||||
"dispose: () => void;","Dispose method"
|
||||
"dispose ... ');\n }","Dispose method"
|
||||
"dispose ... ();\n }","Dispose method"
|
||||
"public ... ();\n }","Dispose method"
|
||||
"readonl ... > void;","Dispose method"
|
||||
"dispose(): unknown","Dispose method"
|
||||
"dispose ... inonSpy","Dispose method"
|
||||
"dispose ... inonSpy","Dispose method"
|
||||
"dispose ... inonSpy","Dispose method"
|
||||
"dispose ... inonSpy","Dispose method"
|
||||
|
@@ -0,0 +1,19 @@
|
||||
## github/vscode-codeql
|
||||
|
||||
| - | Message |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
|
||||
| [dispose ... ();\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/cli.ts#L211) | Dispose method |
|
||||
| [readonl ... > void;](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/extension.ts#L166) | Dispose method |
|
||||
| [async d ... }\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/logging.ts#L151) | Dispose method |
|
||||
| [dispose(): any;](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/pure/disposable-object.ts#L5) | Dispose method |
|
||||
| [public ... }\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/pure/disposable-object.ts#L65) | Dispose method |
|
||||
| [dispose: () => void;](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/query-results.ts#L54) | Dispose method |
|
||||
| [dispose ... ');\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/queryserver-client.ts#L32) | Dispose method |
|
||||
| [dispose ... ();\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/telemetry.ts#L129) | Dispose method |
|
||||
| [public ... ();\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/test-ui.ts#L54) | Dispose method |
|
||||
| [readonl ... > void;](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/run-queries.ts#L327) | Dispose method |
|
||||
| [dispose(): unknown](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts#L150) | Dispose method |
|
||||
| [dispose ... inonSpy](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/test/pure-tests/disposable-object.test.ts#L12) | Dispose method |
|
||||
| [dispose ... inonSpy](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/test/pure-tests/disposable-object.test.ts#L13) | Dispose method |
|
||||
| [dispose ... inonSpy](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/test/pure-tests/disposable-object.test.ts#L14) | Dispose method |
|
||||
| [dispose ... inonSpy](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/test/pure-tests/disposable-object.test.ts#L15) | Dispose method |
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
github/vscode-codeql
|
||||
@@ -0,0 +1 @@
|
||||
15
|
||||
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
"md","col1"
|
||||
"dispose ... ();\n }","Dispose method"
|
||||
"readonl ... > void;","Dispose method"
|
||||
"async d ... }\n }","Dispose method"
|
||||
"dispose(): any;","Dispose method"
|
||||
"public ... }\n }","Dispose method"
|
||||
"dispose: () => void;","Dispose method"
|
||||
"dispose ... ');\n }","Dispose method"
|
||||
"dispose ... ();\n }","Dispose method"
|
||||
"public ... ();\n }","Dispose method"
|
||||
"readonl ... > void;","Dispose method"
|
||||
"dispose(): unknown","Dispose method"
|
||||
"dispose ... inonSpy","Dispose method"
|
||||
"dispose ... inonSpy","Dispose method"
|
||||
"dispose ... inonSpy","Dispose method"
|
||||
"dispose ... inonSpy","Dispose method"
|
||||
|
@@ -0,0 +1,19 @@
|
||||
## github/vscode-codeql
|
||||
|
||||
| - | Message |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
|
||||
| [dispose ... ();\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/cli.ts#L211) | Dispose method |
|
||||
| [readonl ... > void;](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/extension.ts#L166) | Dispose method |
|
||||
| [async d ... }\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/logging.ts#L151) | Dispose method |
|
||||
| [dispose(): any;](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/pure/disposable-object.ts#L5) | Dispose method |
|
||||
| [public ... }\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/pure/disposable-object.ts#L65) | Dispose method |
|
||||
| [dispose: () => void;](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/query-results.ts#L54) | Dispose method |
|
||||
| [dispose ... ');\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/queryserver-client.ts#L32) | Dispose method |
|
||||
| [dispose ... ();\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/telemetry.ts#L129) | Dispose method |
|
||||
| [public ... ();\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/test-ui.ts#L54) | Dispose method |
|
||||
| [readonl ... > void;](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/run-queries.ts#L327) | Dispose method |
|
||||
| [dispose(): unknown](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/vscode-tests/no-workspace/helpers.test.ts#L150) | Dispose method |
|
||||
| [dispose ... inonSpy](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/test/pure-tests/disposable-object.test.ts#L12) | Dispose method |
|
||||
| [dispose ... inonSpy](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/test/pure-tests/disposable-object.test.ts#L13) | Dispose method |
|
||||
| [dispose ... inonSpy](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/test/pure-tests/disposable-object.test.ts#L14) | Dispose method |
|
||||
| [dispose ... inonSpy](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/test/pure-tests/disposable-object.test.ts#L15) | Dispose method |
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"executionEndTime": 1645645080281,
|
||||
"analysisSummaries": [
|
||||
{
|
||||
"nwo": "github/vscode-codeql",
|
||||
"resultCount": 15,
|
||||
"fileSizeInBytes": 191025,
|
||||
"downloadLink": {
|
||||
"id": "171543249",
|
||||
"urlPath": "/repos/avocado-corp/hucairz/actions/artifacts/171543249",
|
||||
"innerFilePath": "results.sarif",
|
||||
"queryId": "MRVA Integration test 1-6sBi6oaky_fxqXW2NA4bx"
|
||||
}
|
||||
},
|
||||
{
|
||||
"nwo": "other/hucairz",
|
||||
"resultCount": 15,
|
||||
"fileSizeInBytes": 191025,
|
||||
"downloadLink": {
|
||||
"id": "11111111",
|
||||
"urlPath": "/repos/avocado-corp/hucairz/actions/artifacts/11111111",
|
||||
"innerFilePath": "results.sarif",
|
||||
"queryId": "MRVA Integration test 1-6sBi6oaky_fxqXW2NA4bx"
|
||||
}
|
||||
}
|
||||
],
|
||||
"analysisFailures": [],
|
||||
"queryId": "MRVA Integration test 1-6sBi6oaky_fxqXW2NA4bx"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"queryName": "MRVA Integration test 1",
|
||||
"queryFilePath": "PLACEHOLDER/q0.ql",
|
||||
"queryText": "/**\n * @name MRVA Integration test 1\n * @kind problem\n * @problem.severity warning\n * @id javascript/integration-test-1\n */\nimport javascript\n\nfrom MemberDeclaration md\nwhere md.getName() = \"dispose\"\nselect md, \"Dispose method\"\n",
|
||||
"controllerRepository": {
|
||||
"owner": "dsp-testing",
|
||||
"name": "qc-run2"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"owner": "github",
|
||||
"name": "vscode-codeql"
|
||||
}
|
||||
],
|
||||
"executionStartTime": 1645644967533,
|
||||
"actionsWorkflowRunId": 1889315769
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
1645644971870
|
||||
@@ -0,0 +1 @@
|
||||
github/vscode-codeql
|
||||
@@ -0,0 +1 @@
|
||||
5
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
"md","col1"
|
||||
"refresh ... d);\n }","Refresh method"
|
||||
"refresh ... <void>;","Refresh method"
|
||||
"public ... }\n }","Refresh method"
|
||||
"public ... }\n }","Refresh method"
|
||||
"refresh ... d);\n }","Refresh method"
|
||||
|
@@ -0,0 +1,9 @@
|
||||
## github/vscode-codeql
|
||||
|
||||
| - | Message |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- |
|
||||
| [refresh ... d);\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/astViewer.ts#L58) | Refresh method |
|
||||
| [refresh ... <void>;](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/databases.ts#L234) | Refresh method |
|
||||
| [public ... }\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/databases.ts#L354) | Refresh method |
|
||||
| [public ... }\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/discovery.ts#L21) | Refresh method |
|
||||
| [refresh ... d);\n }](https://github.com/github/vscode-codeql/blob/c943c89fc694a06e95845c0b7b7c4e71983dd8c4/extensions/ql-vscode/src/query-history.ts#L268) | Refresh method |
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"executionEndTime": 1645645150738,
|
||||
"analysisSummaries": [
|
||||
{
|
||||
"nwo": "github/vscode-codeql",
|
||||
"resultCount": 5,
|
||||
"fileSizeInBytes": 81237,
|
||||
"downloadLink": {
|
||||
"id": "171544171",
|
||||
"urlPath": "/repos/avocado-corp/hucairz/actions/artifacts/171544171",
|
||||
"innerFilePath": "results.sarif",
|
||||
"queryId": "MRVA Integration test 2-UL-vbKAjP8ffObxjsp7hN"
|
||||
}
|
||||
}
|
||||
],
|
||||
"analysisFailures": [],
|
||||
"queryId": "MRVA Integration test 2-UL-vbKAjP8ffObxjsp7hN"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"queryName": "MRVA Integration test 2",
|
||||
"queryFilePath": "PLACEHOLDER/q1.ql",
|
||||
"queryText": "/**\n * @name MRVA Integration test 2\n * @kind problem\n * @problem.severity warning\n * @id javascript/integration-test-2\n */\nimport javascript\n\nfrom MemberDeclaration md\nwhere md.getName() = \"refresh\"\nselect md, \"Refresh method\"\n",
|
||||
"controllerRepository": {
|
||||
"owner": "dsp-testing",
|
||||
"name": "qc-run2"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"owner": "github",
|
||||
"name": "vscode-codeql"
|
||||
}
|
||||
],
|
||||
"executionStartTime": 1645644973911,
|
||||
"actionsWorkflowRunId": 1889316048
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
1645644974055
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": 1,
|
||||
"queries": [
|
||||
{
|
||||
"t": "remote",
|
||||
"status": "Completed",
|
||||
"completed": true,
|
||||
"queryId": "MRVA Integration test 1-6sBi6oaky_fxqXW2NA4bx",
|
||||
"label": "MRVA Integration test 1",
|
||||
"remoteQuery": {
|
||||
"queryName": "MRVA Integration test 1",
|
||||
"queryFilePath": "PLACEHOLDER/q0.ql",
|
||||
"queryText": "/**\n * @name MRVA Integration test 1\n * @kind problem\n * @problem.severity warning\n * @id javascript/integration-test-1\n */\nimport javascript\n\nfrom MemberDeclaration md\nwhere md.getName() = \"dispose\"\nselect md, \"Dispose method\"\n",
|
||||
"controllerRepository": {
|
||||
"owner": "dsp-testing",
|
||||
"name": "qc-run2"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"owner": "github",
|
||||
"name": "vscode-codeql"
|
||||
}
|
||||
],
|
||||
"executionStartTime": 1645644967533,
|
||||
"actionsWorkflowRunId": 1889315769
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": "remote",
|
||||
"status": "Completed",
|
||||
"completed": true,
|
||||
"queryId": "MRVA Integration test 2-UL-vbKAjP8ffObxjsp7hN",
|
||||
"label": "MRVA Integration test 2",
|
||||
"remoteQuery": {
|
||||
"queryName": "MRVA Integration test 2",
|
||||
"queryFilePath": "PLACEHOLDER/q1.ql",
|
||||
"queryText": "/**\n * @name MRVA Integration test 2\n * @kind problem\n * @problem.severity warning\n * @id javascript/integration-test-2\n */\nimport javascript\n\nfrom MemberDeclaration md\nwhere md.getName() = \"refresh\"\nselect md, \"Refresh method\"\n",
|
||||
"controllerRepository": {
|
||||
"owner": "dsp-testing",
|
||||
"name": "qc-run2"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"owner": "github",
|
||||
"name": "vscode-codeql"
|
||||
}
|
||||
],
|
||||
"executionStartTime": 1645644973911,
|
||||
"actionsWorkflowRunId": 1889316048
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import * as sinon from 'sinon';
|
||||
import * as chai from 'chai';
|
||||
import 'mocha';
|
||||
import 'sinon-chai';
|
||||
import * as chaiAsPromised from 'chai-as-promised';
|
||||
|
||||
import { CancellationToken, ExtensionContext, Uri, window, workspace } from 'vscode';
|
||||
import { QueryHistoryConfig } from '../../config';
|
||||
import { DatabaseManager } from '../../databases';
|
||||
import { tmpDir } from '../../helpers';
|
||||
import { QueryHistoryManager } from '../../query-history';
|
||||
import { QueryServerClient } from '../../queryserver-client';
|
||||
import { Credentials } from '../../authentication';
|
||||
import { AnalysesResultsManager } from '../../remote-queries/analyses-results-manager';
|
||||
import { RemoteQueryResult } from '../../remote-queries/shared/remote-query-result';
|
||||
import { DisposableBucket } from '../disposable-bucket';
|
||||
import { testDisposeHandler } from '../test-dispose-handler';
|
||||
import { walk } from '../directory-walker';
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
const expect = chai.expect;
|
||||
|
||||
/**
|
||||
* Tests for remote queries and how they interact with the query history manager.
|
||||
*/
|
||||
|
||||
describe('Remote queries and query history manager', function() {
|
||||
|
||||
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 rawQueryHistory: any;
|
||||
let remoteQueryResult0: RemoteQueryResult;
|
||||
let remoteQueryResult1: RemoteQueryResult;
|
||||
let disposables: DisposableBucket;
|
||||
let showTextDocumentSpy: sinon.SinonSpy;
|
||||
let openTextDocumentSpy: sinon.SinonSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
// 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
|
||||
copyHistoryState();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
deleteHistoryState();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
disposables = new DisposableBucket();
|
||||
|
||||
rawQueryHistory = fs.readJSONSync(path.join(STORAGE_DIR, 'workspace-query-history.json')).queries;
|
||||
remoteQueryResult0 = fs.readJSONSync(path.join(STORAGE_DIR, 'queries', rawQueryHistory[0].queryId, 'query-result.json'));
|
||||
remoteQueryResult1 = fs.readJSONSync(path.join(STORAGE_DIR, 'queries', rawQueryHistory[1].queryId, 'query-result.json'));
|
||||
|
||||
qhm = new QueryHistoryManager(
|
||||
{} as QueryServerClient,
|
||||
{} as DatabaseManager,
|
||||
STORAGE_DIR,
|
||||
{
|
||||
globalStorageUri: Uri.file(STORAGE_DIR),
|
||||
extensionPath: EXTENSION_PATH
|
||||
} as ExtensionContext,
|
||||
{
|
||||
onDidChangeConfiguration: () => new DisposableBucket(),
|
||||
} as unknown as QueryHistoryConfig,
|
||||
asyncNoop
|
||||
);
|
||||
disposables.push(qhm);
|
||||
|
||||
showTextDocumentSpy = sandbox.spy(window, 'showTextDocument');
|
||||
openTextDocumentSpy = sandbox.spy(workspace, 'openTextDocument');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
disposables.dispose(testDisposeHandler);
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('should read query history', async () => {
|
||||
const spy = sandbox.spy();
|
||||
disposables.push(qhm.onDidAddQueryItem(spy));
|
||||
await qhm.readQueryHistory();
|
||||
|
||||
// Should have added the query history. Contents are directly from the file
|
||||
expect(spy.getCall(0).args[0]).to.deep.eq(rawQueryHistory[0]);
|
||||
expect(spy.getCall(1).args[0]).to.deep.eq(rawQueryHistory[1]);
|
||||
expect(spy.callCount).to.eq(2);
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
it('should remove and then add query from history', async () => {
|
||||
await qhm.readQueryHistory();
|
||||
const addSpy = sandbox.spy();
|
||||
disposables.push(qhm.onDidAddQueryItem(addSpy));
|
||||
const removeSpy = sandbox.spy();
|
||||
disposables.push(qhm.onDidRemoveQueryItem(removeSpy));
|
||||
|
||||
// Remove the first query
|
||||
await qhm.handleRemoveHistoryItem(qhm.treeDataProvider.allHistory[0]);
|
||||
expect(removeSpy.getCall(0).args[0]).to.deep.eq(rawQueryHistory[0]);
|
||||
expect(removeSpy.callCount).to.eq(1);
|
||||
expect(addSpy.callCount).to.eq(0);
|
||||
expect(qhm.treeDataProvider.allHistory).to.deep.eq(rawQueryHistory.slice(1));
|
||||
|
||||
// Add it back
|
||||
qhm.addQuery(rawQueryHistory[0]);
|
||||
expect(removeSpy.callCount).to.eq(1);
|
||||
expect(addSpy.getCall(0).args[0]).to.deep.eq(rawQueryHistory[0]);
|
||||
expect(addSpy.callCount).to.eq(1);
|
||||
expect(qhm.treeDataProvider.allHistory).to.deep.eq([rawQueryHistory[1], rawQueryHistory[0]]);
|
||||
});
|
||||
|
||||
it('should remove two queries from history', async () => {
|
||||
await qhm.readQueryHistory();
|
||||
const addSpy = sandbox.spy();
|
||||
disposables.push(qhm.onDidAddQueryItem(addSpy));
|
||||
const removeSpy = sandbox.spy();
|
||||
disposables.push(qhm.onDidRemoveQueryItem(removeSpy));
|
||||
|
||||
// Remove the both queries
|
||||
// Just for fun, let's do it in reverse order
|
||||
await qhm.handleRemoveHistoryItem(undefined!, [qhm.treeDataProvider.allHistory[1], qhm.treeDataProvider.allHistory[0]]);
|
||||
expect(removeSpy.getCall(0).args[0]).to.deep.eq(rawQueryHistory[1]);
|
||||
expect(removeSpy.getCall(1).args[0]).to.deep.eq(rawQueryHistory[0]);
|
||||
expect(qhm.treeDataProvider.allHistory).to.deep.eq([]);
|
||||
expect(removeSpy.callCount).to.eq(2);
|
||||
|
||||
// also, both queries should be removed from on disk storage
|
||||
expect(fs.readJSONSync(path.join(STORAGE_DIR, 'workspace-query-history.json'))).to.deep.eq({
|
||||
version: 1,
|
||||
queries: []
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle a click', async () => {
|
||||
await qhm.readQueryHistory();
|
||||
const openSpy = sandbox.spy();
|
||||
disposables.push(qhm.onWillOpenQueryItem(openSpy));
|
||||
|
||||
await qhm.handleItemClicked(qhm.treeDataProvider.allHistory[0], []);
|
||||
expect(openSpy.getCall(0).args[0]).to.deep.eq(rawQueryHistory[0]);
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
const uri: Uri = openTextDocumentSpy.getCall(0).args[0];
|
||||
expect(uri.scheme).to.eq('codeql');
|
||||
const params = new URLSearchParams(uri.query);
|
||||
expect(params.get('isQuickEval')).to.eq('false');
|
||||
expect(params.get('queryText')).to.eq(rawQueryHistory[0].remoteQuery.queryText);
|
||||
});
|
||||
|
||||
describe('AnalysisResultsManager', () => {
|
||||
|
||||
let mockCredentials: any;
|
||||
let mockOctokit: any;
|
||||
let mockLogger: any;
|
||||
let arm: AnalysesResultsManager;
|
||||
|
||||
beforeEach(() => {
|
||||
mockOctokit = {
|
||||
request: sandbox.stub()
|
||||
};
|
||||
mockCredentials = {
|
||||
getOctokit: () => mockOctokit
|
||||
};
|
||||
mockLogger = {
|
||||
log: sandbox.spy()
|
||||
};
|
||||
sandbox.stub(Credentials, 'initialize').resolves(mockCredentials);
|
||||
|
||||
arm = new AnalysesResultsManager(
|
||||
{} as ExtensionContext,
|
||||
path.join(STORAGE_DIR, 'queries'),
|
||||
mockLogger
|
||||
);
|
||||
});
|
||||
|
||||
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 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;
|
||||
|
||||
// result should have been published twice
|
||||
// first time, it is in progress
|
||||
expect(publisher.getCall(0).args[0][0]).to.include({
|
||||
nwo: 'github/vscode-codeql',
|
||||
status: 'InProgress',
|
||||
// results: ... avoid checking the results 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',
|
||||
// results: ... avoid checking the results object since it is complex
|
||||
});
|
||||
expect(publisher).to.have.been.calledTwice;
|
||||
|
||||
// result should be stored in the manager
|
||||
expect(arm.getAnalysesResults(rawQueryHistory[0].queryId)[0]).to.include({
|
||||
nwo: 'github/vscode-codeql',
|
||||
status: 'Completed',
|
||||
// results: ... avoid checking the results object since it is complex
|
||||
});
|
||||
publisher.resetHistory();
|
||||
|
||||
// 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;
|
||||
});
|
||||
|
||||
it('should download two artifacts at once', async () => {
|
||||
const publisher = sandbox.spy();
|
||||
const analysisSummaries = [...remoteQueryResult0.analysisSummaries];
|
||||
await arm.downloadAnalysesResults(analysisSummaries, undefined, publisher);
|
||||
|
||||
const trimmed = publisher.getCalls().map(call => call.args[0]).map(args => {
|
||||
args.forEach((analysisResult: any) => delete analysisResult.results);
|
||||
return args;
|
||||
});
|
||||
|
||||
// As before, but now both summaries should have been published
|
||||
expect(trimmed[0]).to.deep.eq([{
|
||||
nwo: 'github/vscode-codeql',
|
||||
status: 'InProgress',
|
||||
}]);
|
||||
|
||||
expect(trimmed[1]).to.deep.eq([{
|
||||
nwo: 'github/vscode-codeql',
|
||||
status: 'InProgress',
|
||||
}, {
|
||||
nwo: 'other/hucairz',
|
||||
status: 'InProgress',
|
||||
}]);
|
||||
|
||||
// there is a third call. It is non-deterministic if
|
||||
// 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([{
|
||||
nwo: 'github/vscode-codeql',
|
||||
status: 'Completed',
|
||||
}, {
|
||||
nwo: 'other/hucairz',
|
||||
status: 'Completed',
|
||||
}]);
|
||||
|
||||
expect(publisher).to.have.callCount(4);
|
||||
});
|
||||
|
||||
it('should avoid publishing when the request is cancelled', async () => {
|
||||
const publisher = sandbox.spy();
|
||||
const analysisSummaries = [...remoteQueryResult0.analysisSummaries];
|
||||
|
||||
try {
|
||||
await arm.downloadAnalysesResults(analysisSummaries, {
|
||||
isCancellationRequested: true
|
||||
} as CancellationToken, publisher);
|
||||
expect.fail('Should have thrown');
|
||||
} catch (e) {
|
||||
expect(e.message).to.contain('cancelled');
|
||||
}
|
||||
|
||||
expect(publisher).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should get the analysis results', async () => {
|
||||
const publisher = sandbox.spy();
|
||||
const analysisSummaries0 = [...remoteQueryResult0.analysisSummaries];
|
||||
const analysisSummaries1 = [...remoteQueryResult1.analysisSummaries];
|
||||
|
||||
await arm.downloadAnalysesResults(analysisSummaries0, undefined, publisher);
|
||||
await arm.downloadAnalysesResults(analysisSummaries1, undefined, publisher);
|
||||
|
||||
const result0 = arm.getAnalysesResults(rawQueryHistory[0].queryId);
|
||||
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);
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
// This test is failing on windows in CI.
|
||||
it.skip('should read sarif', async () => {
|
||||
const publisher = sandbox.spy();
|
||||
const analysisSummaries0 = [remoteQueryResult0.analysisSummaries[0]];
|
||||
await arm.downloadAnalysesResults(analysisSummaries0, undefined, publisher);
|
||||
|
||||
const sarif = fs.readJSONSync(path.join(STORAGE_DIR, 'queries', rawQueryHistory[0].queryId, '171543249', 'results.sarif'));
|
||||
const queryResults = sarif.runs
|
||||
.flatMap((run: any) => run.results)
|
||||
.map((result: any) => ({ message: result.message.text }));
|
||||
|
||||
expect(publisher.getCall(1).args[0][0].results).to.deep.eq(queryResults);
|
||||
});
|
||||
});
|
||||
|
||||
function copyHistoryState() {
|
||||
fs.ensureDirSync(STORAGE_DIR);
|
||||
fs.copySync(path.join(__dirname, 'data/remote-queries/'), path.join(tmpDir.name, 'remote-queries'));
|
||||
|
||||
// also, replace the files with "PLACEHOLDER" so that they have the correct directory
|
||||
for (const p of walk(STORAGE_DIR)) {
|
||||
replacePlaceholder(path.join(p));
|
||||
}
|
||||
}
|
||||
|
||||
function deleteHistoryState() {
|
||||
fs.removeSync(STORAGE_DIR);
|
||||
}
|
||||
|
||||
function replacePlaceholder(filePath: string) {
|
||||
if (filePath.endsWith('.json')) {
|
||||
const newContents = fs.readFileSync(filePath, 'utf8').replaceAll('PLACEHOLDER', STORAGE_DIR.replaceAll('\\', '/'));
|
||||
fs.writeFileSync(filePath, newContents, 'utf8');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -6,7 +6,7 @@
|
||||
"module": "commonjs",
|
||||
"target": "es2017",
|
||||
"outDir": "out",
|
||||
"lib": ["ES2020"],
|
||||
"lib": ["ES2021"],
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
|
||||
Reference in New Issue
Block a user