Skip to content

Commit 71611e0

Browse files
authored
Add config options for default sorting/filtering values in variant analysis results view (#2392)
1 parent 70a9ee6 commit 71611e0

File tree

14 files changed

+192
-69
lines changed

14 files changed

+192
-69
lines changed

extensions/ql-vscode/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## [UNRELEASED]
44

5+
- Add settings `codeQL.variantAnalysis.defaultResultsFilter` and `codeQL.variantAnalysis.defaultResultsSort` for configuring how variant analysis results are filtered and sorted in the results view. The default is to show all repositories, and to sort by the number of results. [#2392](https://github.com/github/vscode-codeql/pull/2392)
6+
57
## 1.8.4 - 3 May 2023
68

79
- Avoid repeated error messages when unable to monitor a variant analysis. [#2396](https://github.com/github/vscode-codeql/pull/2396)

extensions/ql-vscode/package.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,36 @@
329329
"patternErrorMessage": "Please enter a valid GitHub repository",
330330
"markdownDescription": "[For internal use only] The name of the GitHub repository in which the GitHub Actions workflow is run when using the \"Run Variant Analysis\" command. The repository should be of the form `<owner>/<repo>`)."
331331
},
332+
"codeQL.variantAnalysis.defaultResultsFilter": {
333+
"type": "string",
334+
"default": "all",
335+
"enum": [
336+
"all",
337+
"withResults"
338+
],
339+
"enumDescriptions": [
340+
"Show all repositories in the results view.",
341+
"Show only repositories with results in the results view."
342+
],
343+
"description": "The default filter to apply to the variant analysis results view."
344+
},
345+
"codeQL.variantAnalysis.defaultResultsSort": {
346+
"type": "string",
347+
"default": "numberOfResults",
348+
"enum": [
349+
"alphabetically",
350+
"popularity",
351+
"mostRecentCommit",
352+
"numberOfResults"
353+
],
354+
"enumDescriptions": [
355+
"Sort repositories alphabetically in the results view.",
356+
"Sort repositories by popularity in the results view.",
357+
"Sort repositories by most recent commit in the results view.",
358+
"Sort repositories by number of results in the results view."
359+
],
360+
"description": "The default sorting order for repositories in the variant analysis results view."
361+
},
332362
"codeQL.logInsights.joinOrderWarningThreshold": {
333363
"type": "number",
334364
"default": 50,

extensions/ql-vscode/src/config.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import {
99
import { DistributionManager } from "./codeql-cli/distribution";
1010
import { extLogger } from "./common";
1111
import { ONE_DAY_IN_MS } from "./pure/time";
12+
import {
13+
FilterKey,
14+
SortKey,
15+
defaultFilterSortState,
16+
} from "./pure/variant-analysis-filter-sort";
1217

1318
export const ALL_SETTINGS: Setting[] = [];
1419

@@ -541,6 +546,34 @@ export class VariantAnalysisConfigListener
541546
}
542547
}
543548

549+
const VARIANT_ANALYSIS_FILTER_RESULTS = new Setting(
550+
"defaultResultsFilter",
551+
VARIANT_ANALYSIS_SETTING,
552+
);
553+
554+
export function getVariantAnalysisDefaultResultsFilter(): FilterKey {
555+
const value = VARIANT_ANALYSIS_FILTER_RESULTS.getValue<string>();
556+
if (Object.values(FilterKey).includes(value as FilterKey)) {
557+
return value as FilterKey;
558+
} else {
559+
return defaultFilterSortState.filterKey;
560+
}
561+
}
562+
563+
const VARIANT_ANALYSIS_SORT_RESULTS = new Setting(
564+
"defaultResultsSort",
565+
VARIANT_ANALYSIS_SETTING,
566+
);
567+
568+
export function getVariantAnalysisDefaultResultsSort(): SortKey {
569+
const value = VARIANT_ANALYSIS_SORT_RESULTS.getValue<string>();
570+
if (Object.values(SortKey).includes(value as SortKey)) {
571+
return value as SortKey;
572+
} else {
573+
return defaultFilterSortState.sortKey;
574+
}
575+
}
576+
544577
/**
545578
* The branch of "github/codeql-variant-analysis-action" to use with the "Run Variant Analysis" command.
546579
* Default value is "main".

extensions/ql-vscode/src/pure/interface-types.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import {
1111
VariantAnalysisScannedRepositoryResult,
1212
VariantAnalysisScannedRepositoryState,
1313
} from "../variant-analysis/shared/variant-analysis";
14-
import { RepositoriesFilterSortStateWithIds } from "./variant-analysis-filter-sort";
14+
import {
15+
RepositoriesFilterSortState,
16+
RepositoriesFilterSortStateWithIds,
17+
} from "./variant-analysis-filter-sort";
1518
import { ErrorLike } from "./errors";
1619
import { DataFlowPaths } from "../variant-analysis/shared/data-flow-paths";
1720
import { ExternalApiUsage } from "../data-extensions-editor/external-api-usage";
@@ -407,6 +410,11 @@ export interface SetVariantAnalysisMessage {
407410
variantAnalysis: VariantAnalysis;
408411
}
409412

413+
export interface SetFilterSortStateMessage {
414+
t: "setFilterSortState";
415+
filterSortState: RepositoriesFilterSortState;
416+
}
417+
410418
export type VariantAnalysisState = {
411419
variantAnalysisId: number;
412420
};
@@ -459,6 +467,7 @@ export interface ShowDataFlowPathsMessage {
459467

460468
export type ToVariantAnalysisMessage =
461469
| SetVariantAnalysisMessage
470+
| SetFilterSortStateMessage
462471
| SetRepoResultsMessage
463472
| SetRepoStatesMessage;
464473

extensions/ql-vscode/src/pure/variant-analysis-filter-sort.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export type RepositoriesFilterSortStateWithIds = RepositoriesFilterSortState & {
3030
export const defaultFilterSortState: RepositoriesFilterSortState = {
3131
searchValue: "",
3232
filterKey: FilterKey.All,
33-
sortKey: SortKey.Alphabetically,
33+
sortKey: SortKey.NumberOfResults,
3434
};
3535

3636
export function matchesFilter(

extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ import { redactableError } from "../pure/errors";
2727
import { DataFlowPathsView } from "./data-flow-paths-view";
2828
import { DataFlowPaths } from "./shared/data-flow-paths";
2929
import { App } from "../common/app";
30+
import {
31+
getVariantAnalysisDefaultResultsFilter,
32+
getVariantAnalysisDefaultResultsSort,
33+
} from "../config";
3034

3135
export class VariantAnalysisView
3236
extends AbstractWebview<ToVariantAnalysisMessage, FromVariantAnalysisMessage>
@@ -186,11 +190,22 @@ export class VariantAnalysisView
186190
return;
187191
}
188192

193+
const filterSortState = {
194+
searchValue: "",
195+
filterKey: getVariantAnalysisDefaultResultsFilter(),
196+
sortKey: getVariantAnalysisDefaultResultsSort(),
197+
};
198+
189199
await this.postMessage({
190200
t: "setVariantAnalysis",
191201
variantAnalysis,
192202
});
193203

204+
await this.postMessage({
205+
t: "setFilterSortState",
206+
filterSortState,
207+
});
208+
194209
const repoStates = await this.manager.getRepoStates(this.variantAnalysisId);
195210
if (repoStates.length === 0) {
196211
return;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { act } from "@testing-library/react";
2+
3+
/** Helper function used in tests */
4+
export async function postMessage<T>(msg: T): Promise<void> {
5+
await act(async () => {
6+
// window.postMessage doesn't set the origin correctly, see
7+
// https://github.com/jsdom/jsdom/issues/2745
8+
window.dispatchEvent(
9+
new MessageEvent("message", {
10+
source: window,
11+
origin: window.location.origin,
12+
data: msg,
13+
}),
14+
);
15+
16+
// The event is dispatched asynchronously, so we need to wait for it to be handled.
17+
await new Promise((resolve) => setTimeout(resolve, 0));
18+
});
19+
}

extensions/ql-vscode/src/view/results/__tests__/results.spec.tsx

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from "react";
2-
import { act, render as reactRender, screen } from "@testing-library/react";
2+
import { render as reactRender, screen } from "@testing-library/react";
33
import { ResultsApp } from "../results";
44
import {
55
Interpretation,
@@ -9,6 +9,7 @@ import {
99
import * as fs from "fs-extra";
1010
import { resolve } from "path";
1111
import { ColumnKindCode } from "../../../pure/bqrs-cli-types";
12+
import { postMessage } from "../../common/post-message";
1213

1314
const exampleSarif = fs.readJSONSync(
1415
resolve(
@@ -19,22 +20,6 @@ const exampleSarif = fs.readJSONSync(
1920

2021
describe(ResultsApp.name, () => {
2122
const render = () => reactRender(<ResultsApp />);
22-
const postMessage = async (msg: IntoResultsViewMsg) => {
23-
await act(async () => {
24-
// window.postMessage doesn't set the origin correctly, see
25-
// https://github.com/jsdom/jsdom/issues/2745
26-
window.dispatchEvent(
27-
new MessageEvent("message", {
28-
source: window,
29-
origin: window.location.origin,
30-
data: msg,
31-
}),
32-
);
33-
34-
// The event is dispatched asynchronously, so we need to wait for it to be handled.
35-
await new Promise((resolve) => setTimeout(resolve, 0));
36-
});
37-
};
3823

3924
it("renders results", async () => {
4025
render();
@@ -98,13 +83,13 @@ describe(ResultsApp.name, () => {
9883
},
9984
};
10085

101-
await postMessage(message);
86+
await postMessage<IntoResultsViewMsg>(message);
10287

10388
expect(
10489
screen.getByText("'x' is assigned a value but never used."),
10590
).toBeInTheDocument();
10691

107-
await postMessage({
92+
await postMessage<IntoResultsViewMsg>({
10893
...message,
10994
t: "showInterpretedPage",
11095
pageNumber: 1,
@@ -124,7 +109,7 @@ describe(ResultsApp.name, () => {
124109
it("renders results when switching between queries with different result set names", async () => {
125110
render();
126111

127-
await postMessage({
112+
await postMessage<IntoResultsViewMsg>({
128113
t: "setState",
129114
interpretation: undefined,
130115
origResultsPaths: {
@@ -162,7 +147,7 @@ describe(ResultsApp.name, () => {
162147

163148
expect(screen.getByText("foobar1")).toBeInTheDocument();
164149

165-
await postMessage({
150+
await postMessage<IntoResultsViewMsg>({
166151
t: "setState",
167152
interpretation: undefined,
168153
origResultsPaths: {

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ export function VariantAnalysis({
8787
vscode.setState({
8888
variantAnalysisId: msg.variantAnalysis.id,
8989
});
90+
} else if (msg.t === "setFilterSortState") {
91+
setFilterSortState(msg.filterSortState);
9092
} else if (msg.t === "setRepoResults") {
9193
setRepoResults((oldRepoResults) => {
9294
const newRepoIds = msg.repoResults.map((r) => r.repositoryId);

extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import * as React from "react";
2-
import { render as reactRender, screen } from "@testing-library/react";
2+
import { render as reactRender, screen, waitFor } from "@testing-library/react";
33
import {
44
VariantAnalysisFailureReason,
55
VariantAnalysisStatus,
66
} from "../../../variant-analysis/shared/variant-analysis";
77
import { VariantAnalysis, VariantAnalysisProps } from "../VariantAnalysis";
88
import { createMockVariantAnalysis } from "../../../../test/factories/variant-analysis/shared/variant-analysis";
9+
import { ToVariantAnalysisMessage } from "../../../pure/interface-types";
10+
import { FilterKey, SortKey } from "../../../pure/variant-analysis-filter-sort";
11+
import { postMessage } from "../../common/post-message";
912

1013
describe(VariantAnalysis.name, () => {
1114
const render = (props: Partial<VariantAnalysisProps> = {}) =>
@@ -46,4 +49,29 @@ describe(VariantAnalysis.name, () => {
4649
),
4750
).toBeInTheDocument();
4851
});
52+
53+
it("renders results view with correct filter and sort state", async () => {
54+
const variantAnalysis = createMockVariantAnalysis({});
55+
render({ variantAnalysis });
56+
57+
await waitFor(() => screen.getByDisplayValue("All"));
58+
await waitFor(() => screen.getByDisplayValue("Number of results"));
59+
60+
await postMessage<ToVariantAnalysisMessage>({
61+
t: "setFilterSortState",
62+
filterSortState: {
63+
searchValue: "",
64+
filterKey: FilterKey.WithResults,
65+
sortKey: SortKey.Alphabetically,
66+
},
67+
});
68+
69+
expect(screen.getByDisplayValue("With results")).toBeInTheDocument();
70+
expect(screen.getByDisplayValue("Alphabetically")).toBeInTheDocument();
71+
72+
expect(screen.queryByDisplayValue("All")).not.toBeInTheDocument();
73+
expect(
74+
screen.queryByDisplayValue("Number of results"),
75+
).not.toBeInTheDocument();
76+
});
4977
});

0 commit comments

Comments
 (0)