Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions extensions/ql-vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [UNRELEASED]

- Add settings `codeQL.variantAnalysis.filterResults` and `codeQL.variantAnalysis.sortResults` for configuring how variant analysis results are filtered and sorted in the results view. The default is to show only repositories with results, and to sort by the number of results. [#2392](https://github.com/github/vscode-codeql/pull/2392)
- On the variant analysis results page, show the count of successful analyses instead of completed analyses, and indicate the reason why analyses were not successful. [#2349](https://github.com/github/vscode-codeql/pull/2349)
- Fix bug where the "CodeQL: Set Current Database" command didn't always select the database. [#2384](https://github.com/github/vscode-codeql/pull/2384)

Expand Down
30 changes: 30 additions & 0 deletions extensions/ql-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,36 @@
"patternErrorMessage": "Please enter a valid GitHub repository",
"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>`)."
},
"codeQL.variantAnalysis.filterResults": {
Comment thread
shati-patel marked this conversation as resolved.
Outdated
"type": "string",
"default": "withResults",
"enum": [
"all",
"withResults"
],
"enumDescriptions": [
"Show all repositories in the results view.",
"Show only repositories with results in the results view."
],
"description": "The filter to apply to the variant analysis results view."
Comment thread
shati-patel marked this conversation as resolved.
Outdated
},
"codeQL.variantAnalysis.sortResults": {
"type": "string",
"default": "numberOfResults",
"enum": [
"alphabetically",
"popularity",
"mostRecentCommit",
"numberOfResults"
],
"enumDescriptions": [
"Sort repositories alphabetically in the results view.",
"Sort repositories by popularity in the results view.",
"Sort repositories by most recent commit in the results view.",
"Sort repositories by number of results in the results view."
],
"description": "The default sorting order for repositories in the variant analysis results view."
},
"codeQL.logInsights.joinOrderWarningThreshold": {
"type": "number",
"default": 50,
Expand Down
33 changes: 33 additions & 0 deletions extensions/ql-vscode/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import {
import { DistributionManager } from "./codeql-cli/distribution";
import { extLogger } from "./common";
import { ONE_DAY_IN_MS } from "./pure/time";
import {
FilterKey,
SortKey,
defaultFilterSortState,
} from "./pure/variant-analysis-filter-sort";

export const ALL_SETTINGS: Setting[] = [];

Expand Down Expand Up @@ -529,6 +534,34 @@ export class VariantAnalysisConfigListener
}
}

const VARIANT_ANALYSIS_FILTER_RESULTS = new Setting(
"filterResults",
VARIANT_ANALYSIS_SETTING,
);

export function getVariantAnalysisFilterResults(): FilterKey {
const value = VARIANT_ANALYSIS_FILTER_RESULTS.getValue<string>();
if (Object.values(FilterKey).includes(value as FilterKey)) {
return value as FilterKey;
} else {
return defaultFilterSortState.filterKey;
}
}

const VARIANT_ANALYSIS_SORT_RESULTS = new Setting(
"sortResults",
VARIANT_ANALYSIS_SETTING,
);

export function getVariantAnalysisSortResults(): SortKey {
const value = VARIANT_ANALYSIS_SORT_RESULTS.getValue<string>();
if (Object.values(SortKey).includes(value as SortKey)) {
return value as SortKey;
} else {
return defaultFilterSortState.sortKey;
}
}

/**
* The branch of "github/codeql-variant-analysis-action" to use with the "Run Variant Analysis" command.
* Default value is "main".
Expand Down
6 changes: 5 additions & 1 deletion extensions/ql-vscode/src/pure/interface-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import {
VariantAnalysisScannedRepositoryResult,
VariantAnalysisScannedRepositoryState,
} from "../variant-analysis/shared/variant-analysis";
import { RepositoriesFilterSortStateWithIds } from "./variant-analysis-filter-sort";
import {
RepositoriesFilterSortState,
RepositoriesFilterSortStateWithIds,
} from "./variant-analysis-filter-sort";
import { ErrorLike } from "./errors";
import { DataFlowPaths } from "../variant-analysis/shared/data-flow-paths";
import { ExternalApiUsage } from "../data-extensions-editor/external-api-usage";
Expand Down Expand Up @@ -405,6 +408,7 @@ export interface ParsedResultSets {
export interface SetVariantAnalysisMessage {
t: "setVariantAnalysis";
variantAnalysis: VariantAnalysis;
filterSortState?: RepositoriesFilterSortState;
}

export type VariantAnalysisState = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export type RepositoriesFilterSortStateWithIds = RepositoriesFilterSortState & {

export const defaultFilterSortState: RepositoriesFilterSortState = {
searchValue: "",
filterKey: FilterKey.All,
sortKey: SortKey.Alphabetically,
filterKey: FilterKey.WithResults,
sortKey: SortKey.NumberOfResults,
};

export function matchesFilter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ import { redactableError } from "../pure/errors";
import { DataFlowPathsView } from "./data-flow-paths-view";
import { DataFlowPaths } from "./shared/data-flow-paths";
import { App } from "../common/app";
import {
getVariantAnalysisFilterResults,
getVariantAnalysisSortResults,
} from "../config";

export class VariantAnalysisView
extends AbstractWebview<ToVariantAnalysisMessage, FromVariantAnalysisMessage>
Expand Down Expand Up @@ -186,9 +190,16 @@ export class VariantAnalysisView
return;
}

const filterSortState = {
searchValue: "",
filterKey: getVariantAnalysisFilterResults(),
sortKey: getVariantAnalysisSortResults(),
};

await this.postMessage({
t: "setVariantAnalysis",
variantAnalysis,
filterSortState,
});

const repoStates = await this.manager.getRepoStates(this.variantAnalysisId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ export function VariantAnalysis({
const msg: ToVariantAnalysisMessage = evt.data;
if (msg.t === "setVariantAnalysis") {
setVariantAnalysis(msg.variantAnalysis);
if (msg.filterSortState) {
setFilterSortState(msg.filterSortState);
}
vscode.setState({
variantAnalysisId: msg.variantAnalysis.id,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as React from "react";
import { render as reactRender, screen } from "@testing-library/react";
import { act, render as reactRender, screen } from "@testing-library/react";
import {
VariantAnalysisFailureReason,
VariantAnalysisStatus,
} from "../../../variant-analysis/shared/variant-analysis";
import { VariantAnalysis, VariantAnalysisProps } from "../VariantAnalysis";
import { createMockVariantAnalysis } from "../../../../test/factories/variant-analysis/shared/variant-analysis";
import { ToVariantAnalysisMessage } from "../../../pure/interface-types";
import { FilterKey, SortKey } from "../../../pure/variant-analysis-filter-sort";

describe(VariantAnalysis.name, () => {
const render = (props: Partial<VariantAnalysisProps> = {}) =>
Expand All @@ -16,6 +18,23 @@ describe(VariantAnalysis.name, () => {
/>,
);

const postMessage = async (msg: ToVariantAnalysisMessage) => {
await act(async () => {
// window.postMessage doesn't set the origin correctly, see
// https://github.com/jsdom/jsdom/issues/2745
window.dispatchEvent(
new MessageEvent("message", {
source: window,
origin: window.location.origin,
data: msg,
}),
);

// The event is dispatched asynchronously, so we need to wait for it to be handled.
await new Promise((resolve) => setTimeout(resolve, 0));
});
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we're using this in at least two tests, do you think it would make sense to move this to a separate file and export the function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! I wasn't sure where best to put the shared file. Currently in src/view/common/post-message.ts but I'm open to better suggestions 😅


it("renders a pending analysis", () => {
const variantAnalysis = createMockVariantAnalysis({
status: VariantAnalysisStatus.InProgress,
Expand Down Expand Up @@ -46,4 +65,33 @@ describe(VariantAnalysis.name, () => {
),
).toBeInTheDocument();
});

it("renders results view with correct filter and sort state", async () => {
const variantAnalysis = createMockVariantAnalysis({});
render({ variantAnalysis });

// Without this await, `getByDisplayValue` could not find any selected dropdown option.
await new Promise((resolve) => setTimeout(resolve, 0));

expect(screen.getByDisplayValue("With results")).toBeInTheDocument();
expect(screen.getByDisplayValue("Number of results")).toBeInTheDocument();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, await new Promise((resolve) => setTimeout(resolve, 0)); was necessary to get these "default" expectations to pass 🤔

Not sure why, but it works 😅 If anyone knows more about this, let us know!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why this is happening (it could be that some of the VS Code UI toolkit shadow DOM is only rendered on the next tick or something like that), but a slightly more reliable way to solve this is by waiting for the element to start existing:

await waitFor(() => screen.getByText("With results"));

This way we don't depend on the exact tick and it will still fail if it doesn't find the element after some time.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is perfect! Thanks ✨


await postMessage({
t: "setVariantAnalysis",
variantAnalysis,
filterSortState: {
searchValue: "",
filterKey: FilterKey.All,
sortKey: SortKey.Alphabetically,
},
});

expect(screen.getByDisplayValue("All")).toBeInTheDocument();
expect(screen.getByDisplayValue("Alphabetically")).toBeInTheDocument();

expect(screen.queryByDisplayValue("With results")).not.toBeInTheDocument();
expect(
screen.queryByDisplayValue("Number of results"),
).not.toBeInTheDocument();
});
});