diff --git a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisStats.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisStats.stories.tsx
index 90ff4c5e872..49366e5158d 100644
--- a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisStats.stories.tsx
+++ b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisStats.stories.tsx
@@ -34,6 +34,9 @@ export const Starting = Template.bind({});
Starting.args = {
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
totalRepositoryCount: 10,
+ completedRepositoryCount: 0,
+ successfulRepositoryCount: 0,
+ skippedRepositoryCount: 0,
};
export const Started = Template.bind({});
@@ -41,12 +44,20 @@ Started.args = {
...Starting.args,
resultCount: 99_999,
completedRepositoryCount: 2,
+ successfulRepositoryCount: 2,
};
-export const StartedWithWarnings = Template.bind({});
-StartedWithWarnings.args = {
+export const StartedWithSkippedRepositories = Template.bind({});
+StartedWithSkippedRepositories.args = {
...Starting.args,
- hasWarnings: true,
+ skippedRepositoryCount: 3,
+};
+
+export const StartedWithFailedAnalyses = Template.bind({});
+StartedWithFailedAnalyses.args = {
+ ...Starting.args,
+ completedRepositoryCount: 5,
+ successfulRepositoryCount: 3,
};
export const Succeeded = Template.bind({});
@@ -54,17 +65,35 @@ Succeeded.args = {
...Started.args,
totalRepositoryCount: 1000,
completedRepositoryCount: 1000,
+ successfulRepositoryCount: 1000,
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
createdAt: new Date(1661262726000),
completedAt: new Date(1661263446000),
};
-export const SucceededWithWarnings = Template.bind({});
-SucceededWithWarnings.args = {
+export const SucceededWithSkippedRepositories = Template.bind({});
+SucceededWithSkippedRepositories.args = {
...Succeeded.args,
totalRepositoryCount: 10,
- completedRepositoryCount: 2,
- hasWarnings: true,
+ completedRepositoryCount: 10,
+ successfulRepositoryCount: 10,
+ skippedRepositoryCount: 6,
+};
+
+export const SucceededWithFailedAnalyses = Template.bind({});
+SucceededWithFailedAnalyses.args = {
+ ...Succeeded.args,
+ totalRepositoryCount: 10,
+ completedRepositoryCount: 10,
+ successfulRepositoryCount: 7,
+};
+
+export const SucceededWithFailedAnalysesAndSkippedRepositories = Template.bind(
+ {},
+);
+SucceededWithFailedAnalysesAndSkippedRepositories.args = {
+ ...SucceededWithFailedAnalyses.args,
+ skippedRepositoryCount: 6,
};
export const Failed = Template.bind({});
@@ -77,6 +106,7 @@ Failed.args = {
export const Stopped = Template.bind({});
Stopped.args = {
- ...SucceededWithWarnings.args,
+ ...Started.args,
variantAnalysisStatus: VariantAnalysisStatus.Canceled,
+ completedRepositoryCount: 10,
};
diff --git a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts
index a370ddc5940..7674193d762 100644
--- a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts
+++ b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts
@@ -214,6 +214,16 @@ export function hasRepoScanCompleted(
return isCompletedAnalysisRepoStatus(repo.analysisStatus);
}
+/**
+ * @param repo
+ * @returns whether the repo scan completed successfully
+ */
+export function isRepoScanSuccessful(
+ repo: VariantAnalysisScannedRepository,
+): boolean {
+ return repo.analysisStatus === VariantAnalysisRepoStatus.Succeeded;
+}
+
/**
* @param repo
* @returns whether the repo scan has an artifact that can be downloaded
diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx
index 9661acc5630..a579a426895 100644
--- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx
+++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx
@@ -5,6 +5,7 @@ import {
getSkippedRepoCount,
getTotalResultCount,
hasRepoScanCompleted,
+ isRepoScanSuccessful,
VariantAnalysis,
VariantAnalysisScannedRepositoryDownloadStatus,
VariantAnalysisScannedRepositoryState,
@@ -69,11 +70,17 @@ export const VariantAnalysisHeader = ({
?.length ?? 0
);
}, [variantAnalysis.scannedRepos]);
+ const successfulRepositoryCount = useMemo(() => {
+ return (
+ variantAnalysis.scannedRepos?.filter((repo) => isRepoScanSuccessful(repo))
+ ?.length ?? 0
+ );
+ }, [variantAnalysis.scannedRepos]);
const resultCount = useMemo(() => {
return getTotalResultCount(variantAnalysis.scannedRepos);
}, [variantAnalysis.scannedRepos]);
- const hasSkippedRepos = useMemo(() => {
- return getSkippedRepoCount(variantAnalysis.skippedRepos) > 0;
+ const skippedRepositoryCount = useMemo(() => {
+ return getSkippedRepoCount(variantAnalysis.skippedRepos);
}, [variantAnalysis.skippedRepos]);
const filteredRepositories = useMemo(() => {
return filterAndSortRepositoriesWithResults(variantAnalysis.scannedRepos, {
@@ -130,8 +137,9 @@ export const VariantAnalysisHeader = ({
variantAnalysisStatus={variantAnalysis.status}
totalRepositoryCount={totalScannedRepositoryCount}
completedRepositoryCount={completedRepositoryCount}
+ successfulRepositoryCount={successfulRepositoryCount}
+ skippedRepositoryCount={skippedRepositoryCount}
resultCount={resultCount}
- hasWarnings={hasSkippedRepos}
createdAt={parseDate(variantAnalysis.createdAt)}
completedAt={parseDate(variantAnalysis.completedAt)}
onViewLogsClick={onViewLogsClick}
diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisRepositoriesStats.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisRepositoriesStats.tsx
index bd6cbf48857..39e99fe390e 100644
--- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisRepositoriesStats.tsx
+++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisRepositoriesStats.tsx
@@ -12,43 +12,78 @@ type Props = {
variantAnalysisStatus: VariantAnalysisStatus;
totalRepositoryCount: number;
- completedRepositoryCount?: number | undefined;
-
- showWarning?: boolean;
+ completedRepositoryCount: number;
+ successfulRepositoryCount: number;
+ skippedRepositoryCount: number;
};
+function getIcon(
+ variantAnalysisStatus: VariantAnalysisStatus,
+ completedRepositoryCount: number,
+ successfulRepositoryCount: number,
+ skippedRepositoryCount: number,
+) {
+ if (successfulRepositoryCount < completedRepositoryCount) {
+ if (variantAnalysisStatus === VariantAnalysisStatus.Canceled) {
+ return (
+ <>
+
+
+ >
+ );
+ } else {
+ return (
+ <>
+
+
+ >
+ );
+ }
+ } else if (skippedRepositoryCount > 0) {
+ return (
+ <>
+
+
+ >
+ );
+ } else if (variantAnalysisStatus === VariantAnalysisStatus.Succeeded) {
+ return (
+ <>
+
+
+ >
+ );
+ } else {
+ return undefined;
+ }
+}
+
export const VariantAnalysisRepositoriesStats = ({
variantAnalysisStatus,
totalRepositoryCount,
- completedRepositoryCount = 0,
- showWarning,
+ completedRepositoryCount,
+ successfulRepositoryCount,
+ skippedRepositoryCount,
}: Props) => {
if (variantAnalysisStatus === VariantAnalysisStatus.Failed) {
return (
<>
0
-
+
>
);
}
return (
<>
- {formatDecimal(completedRepositoryCount)}/
+ {formatDecimal(successfulRepositoryCount)}/
{formatDecimal(totalRepositoryCount)}
- {showWarning && (
- <>
-
-
- >
+ {getIcon(
+ variantAnalysisStatus,
+ completedRepositoryCount,
+ successfulRepositoryCount,
+ skippedRepositoryCount,
)}
- {!showWarning &&
- variantAnalysisStatus === VariantAnalysisStatus.Succeeded && (
- <>
-
-
- >
- )}
>
);
};
diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx
index 25802d20e1a..69ae82be304 100644
--- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx
+++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx
@@ -12,9 +12,9 @@ export type VariantAnalysisStatsProps = {
variantAnalysisStatus: VariantAnalysisStatus;
totalRepositoryCount: number;
- completedRepositoryCount?: number | undefined;
-
- hasWarnings?: boolean;
+ completedRepositoryCount: number;
+ successfulRepositoryCount: number;
+ skippedRepositoryCount: number;
resultCount?: number | undefined;
createdAt: Date;
@@ -32,8 +32,9 @@ const Row = styled.div`
export const VariantAnalysisStats = ({
variantAnalysisStatus,
totalRepositoryCount,
- completedRepositoryCount = 0,
- hasWarnings,
+ completedRepositoryCount,
+ successfulRepositoryCount,
+ skippedRepositoryCount,
resultCount,
createdAt,
completedAt,
@@ -54,13 +55,17 @@ export const VariantAnalysisStats = ({
if (
variantAnalysisStatus === VariantAnalysisStatus.Succeeded &&
- hasWarnings
+ successfulRepositoryCount < completedRepositoryCount
) {
- return "Succeeded warnings";
+ return "Some analyses failed";
}
return "Succeeded";
- }, [variantAnalysisStatus, hasWarnings]);
+ }, [
+ variantAnalysisStatus,
+ successfulRepositoryCount,
+ completedRepositoryCount,
+ ]);
const duration = useMemo(() => {
if (!completedAt) {
@@ -80,7 +85,8 @@ export const VariantAnalysisStats = ({
variantAnalysisStatus={variantAnalysisStatus}
totalRepositoryCount={totalRepositoryCount}
completedRepositoryCount={completedRepositoryCount}
- showWarning={hasWarnings}
+ successfulRepositoryCount={successfulRepositoryCount}
+ skippedRepositoryCount={skippedRepositoryCount}
/>
diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx
index 30cf29b9837..d139461483d 100644
--- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx
+++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx
@@ -19,6 +19,9 @@ describe(VariantAnalysisStats.name, () => {
{
expect(screen.queryAllByText("-").length).toBe(1);
});
- it("renders the number of repositories as a formatted number", () => {
- render({ totalRepositoryCount: 123456, completedRepositoryCount: 654321 });
+ it("renders the number of successful repositories as a formatted number", () => {
+ render({
+ totalRepositoryCount: 123456,
+ successfulRepositoryCount: 654321,
+ });
expect(screen.getByText("654,321/123,456")).toBeInTheDocument();
});
- it("renders a warning icon when has warnings is set", () => {
- render({ hasWarnings: true });
+ it("renders a warning icon when skippedRepositoryCount is greater than zero", () => {
+ render({ skippedRepositoryCount: 4 });
expect(
screen.getByRole("img", {
- name: "Warning",
+ name: "Some repositories were skipped",
}),
).toBeInTheDocument();
});
@@ -59,7 +65,7 @@ describe(VariantAnalysisStats.name, () => {
expect(
screen.getByRole("img", {
- name: "Error",
+ name: "Variant analysis failed",
}),
).toBeInTheDocument();
});
@@ -74,7 +80,68 @@ describe(VariantAnalysisStats.name, () => {
).toBeInTheDocument();
});
- it("renders a view logs link when the variant analysis status is succeeded", () => {
+ it("renders an error icon when the overall variant analysis status is in progress but some analyses failed", () => {
+ render({
+ variantAnalysisStatus: VariantAnalysisStatus.InProgress,
+ completedRepositoryCount: 10,
+ successfulRepositoryCount: 5,
+ });
+
+ expect(
+ screen.getByRole("img", {
+ name: "Some analyses failed",
+ }),
+ ).toBeInTheDocument();
+ });
+
+ it("renders an error icon when the overall variant analysis status is succeeded but some analyses failed", () => {
+ render({
+ variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
+ completedRepositoryCount: 10,
+ successfulRepositoryCount: 5,
+ });
+
+ expect(
+ screen.getByRole("img", {
+ name: "Some analyses failed",
+ }),
+ ).toBeInTheDocument();
+ });
+
+ it("renders an error icon when the overall variant analysis status is canceled and some analyses failed", () => {
+ render({
+ variantAnalysisStatus: VariantAnalysisStatus.Canceled,
+ completedRepositoryCount: 10,
+ successfulRepositoryCount: 5,
+ });
+
+ expect(
+ screen.getByRole("img", {
+ name: "Some analyses were stopped",
+ }),
+ ).toBeInTheDocument();
+ });
+
+ it("renders an error icon when some analyses failed but also some repositories were skipped", () => {
+ render({
+ completedRepositoryCount: 10,
+ successfulRepositoryCount: 5,
+ skippedRepositoryCount: 2,
+ });
+
+ expect(
+ screen.getByRole("img", {
+ name: "Some analyses failed",
+ }),
+ ).toBeInTheDocument();
+ expect(
+ screen.queryByRole("img", {
+ name: "Some repositories were skipped",
+ }),
+ ).not.toBeInTheDocument();
+ });
+
+ it("renders 'View logs' link when the variant analysis status is succeeded", () => {
render({
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
completedAt: new Date(),
@@ -84,38 +151,48 @@ describe(VariantAnalysisStats.name, () => {
expect(onViewLogsClick).toHaveBeenCalledTimes(1);
});
- it("renders a running text when the variant analysis status is in progress", () => {
+ it("renders 'Running' text when the variant analysis status is in progress", () => {
render({ variantAnalysisStatus: VariantAnalysisStatus.InProgress });
expect(screen.getByText("Running")).toBeInTheDocument();
});
- it("renders a failed text when the variant analysis status is failed", () => {
+ it("renders 'Failed' text when the variant analysis status is failed", () => {
render({ variantAnalysisStatus: VariantAnalysisStatus.Failed });
expect(screen.getByText("Failed")).toBeInTheDocument();
});
- it("renders a stopped text when the variant analysis status is canceled", () => {
+ it("renders 'Stopped' text when the variant analysis status is canceled", () => {
render({ variantAnalysisStatus: VariantAnalysisStatus.Canceled });
expect(screen.getByText("Stopped")).toBeInTheDocument();
});
- it("renders a succeeded warnings text when the variant analysis status is succeeded and has warnings", () => {
+ it("renders 'Some analyses failed' text when the overall variant analysis status is succeeded but not all analyses successful", () => {
render({
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
- hasWarnings: true,
+ completedRepositoryCount: 10,
+ successfulRepositoryCount: 5,
});
- expect(screen.getByText("Succeeded warnings")).toBeInTheDocument();
+ expect(screen.getByText("Some analyses failed")).toBeInTheDocument();
});
- it("renders a succeeded text when the variant analysis status is succeeded", () => {
+ it("renders 'Succeeded' text when the variant analysis status is succeeded and successful repository count omitted", () => {
render({ variantAnalysisStatus: VariantAnalysisStatus.Succeeded });
expect(screen.getByText("Succeeded")).toBeInTheDocument();
- expect(screen.queryByText("Succeeded warnings")).not.toBeInTheDocument();
+ });
+
+ it("renders 'Succeeded' text when the variant analysis status is succeeded and successful repository count equals total repository count", () => {
+ render({
+ variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
+ completedRepositoryCount: 10,
+ successfulRepositoryCount: 10,
+ });
+
+ expect(screen.getByText("Succeeded")).toBeInTheDocument();
});
it("does not render the duration when the completedAt is not set", () => {