Bring ClientsGraph up to 100% code coverage

Renamed the API type `ApiClient` to `ApiClientData` to avoid confusing
it with the class of the same name.

Signed-off-by: Mcat12 <newtoncat12@yahoo.com>
This commit is contained in:
Mcat12
2019-06-28 21:39:30 -07:00
parent 20813863d3
commit 18ac8cf526
5 changed files with 216 additions and 8 deletions
+14 -4
View File
@@ -14,7 +14,7 @@ import { Line } from "react-chartjs-2";
import { WithTranslation, withTranslation } from "react-i18next";
import moment from "moment";
import { getIntervalForRange } from "../../util/graphUtils";
import api from "../../util/api";
import api, { ApiClient } from "../../util/api";
import ChartTooltip from "./ChartTooltip";
import { WithAPIData } from "../common/WithAPIData";
import { ChartDataSets, ChartOptions, TimeUnit } from "chart.js";
@@ -242,17 +242,23 @@ export const TranslatedClientsGraph = withTranslation([
"time-ranges"
])(ClientsGraph);
export const ClientsGraphContainer = () => (
export interface ClientsGraphContainerProps {
apiClient: ApiClient;
}
export const ClientsGraphContainer = ({
apiClient
}: ClientsGraphContainerProps) => (
<TimeRangeContext.Consumer>
{context => (
<WithAPIData
apiCall={() =>
context.range
? api.getClientsGraphDb(
? apiClient.getClientsGraphDb(
context.range,
getIntervalForRange(context.range)
)
: api.getClientsGraph()
: apiClient.getClientsGraph()
}
repeatOptions={
context.range
@@ -271,3 +277,7 @@ export const ClientsGraphContainer = () => (
)}
</TimeRangeContext.Consumer>
);
ClientsGraphContainer.defaultProps = {
apiClient: api
};
@@ -17,7 +17,7 @@ import { TimeRangeContext } from "../common/context/TimeRangeContext";
export interface TopBlockedClientsData {
blockedQueries: number;
topClients: Array<ApiClient>;
topClients: Array<ApiClientData>;
}
/**
+1 -1
View File
@@ -17,7 +17,7 @@ import { TimeRangeContext } from "../common/context/TimeRangeContext";
export interface TopClientsData {
totalQueries: number;
topClients: Array<ApiClient>;
topClients: Array<ApiClientData>;
}
/**
@@ -11,10 +11,21 @@
import React from "react";
import { shallow } from "enzyme";
import {
ClientsGraph,
ClientsGraphContainer,
loadingProps,
transformData,
TranslatedClientsGraph
} from "../ClientsGraph";
import {
TimeRange,
TimeRangeContext,
TimeRangeContextType
} from "../../common/context/TimeRangeContext";
import moment from "moment";
import { Line } from "react-chartjs-2";
import { ChartData, ChartTooltipItem } from "chart.js";
import { ApiClient } from "../../../util/api";
const fakeData: ApiClientsGraph = {
over_time: [
@@ -38,6 +49,8 @@ const fakeData: ApiClientsGraph = {
]
};
const tick = global.tick;
it("shows loading indicator correctly", () => {
const wrapper = shallow(<TranslatedClientsGraph {...loadingProps} />).dive();
@@ -62,3 +75,188 @@ it("loads API data correctly", async () => {
expect(props.datasets[1].label).toEqual(fakeData.clients[1].ip);
expect(props.datasets[0].data!.length).toEqual(fakeData.over_time.length - 1);
});
it("should use all of the data if there is a time range set", () => {
const range: TimeRange = {
name: "test",
from: moment().subtract(1, "hour"),
until: moment()
};
const props = transformData(fakeData, range);
expect(props.datasets[0].data!.length).toEqual(fakeData.over_time.length);
});
it("should use hour as the time unit if the time range is less than a day", () => {
const range: TimeRange = {
name: "test",
from: moment().subtract(1, "hour"),
until: moment()
};
const props = transformData(fakeData, range);
expect(props.timeUnit).toEqual("hour");
});
it("should use day as the time unit if the time range is more than a day", () => {
const range: TimeRange = {
name: "test",
from: moment().subtract(2, "days"),
until: moment()
};
const props = transformData(fakeData, range);
expect(props.timeUnit).toEqual("day");
});
it("should show the date range in the tooltip title", () => {
const t = jest.fn(key => key);
const wrapper = shallow(
// @ts-ignore Ignore the missing i18n props
<ClientsGraph {...transformData(fakeData, null)} t={t} />
);
const titleFunc = wrapper.find(Line).props().options!.tooltips!.callbacks!
.title!;
const tooltipItem: ChartTooltipItem = {
datasetIndex: 0,
index: 0,
xLabel: "04:02",
yLabel: ""
};
const result = titleFunc([tooltipItem], {});
expect(result).toEqual("Client activity from {{from}} to {{to}}");
expect(t).toHaveBeenCalledWith("Client activity from {{from}} to {{to}}", {
from: "03:57:00",
to: "04:06:59"
});
});
it("should show the client and count in the tooltip label", () => {
const wrapper = shallow(
<TranslatedClientsGraph {...transformData(fakeData, null)} />
).dive();
const labelFunc = wrapper.find(Line).props().options!.tooltips!.callbacks!
.label!;
const tooltipItem: ChartTooltipItem = {
datasetIndex: 0,
index: 0,
xLabel: "xLabel",
yLabel: "yLabel"
};
const data: ChartData = {
datasets: [
{
label: "datasetLabel"
}
]
};
expect(labelFunc(tooltipItem, data)).toEqual("datasetLabel: yLabel");
});
it("should use the normal API call when there is no time range", () => {
const context: TimeRangeContextType = {
range: null,
update: () => {}
};
const apiClient = ({
getClientsGraph: jest.fn(() => Promise.reject({ isCanceled: true })),
getClientsGraphDb: jest.fn(() => Promise.reject({ isCanceled: true }))
} as any) as ApiClient;
shallow(
<TimeRangeContext.Provider value={context}>
<ClientsGraphContainer apiClient={apiClient} />
</TimeRangeContext.Provider>
)
.dive()
.dive()
.dive();
expect(apiClient.getClientsGraph).toHaveBeenCalled();
expect(apiClient.getClientsGraphDb).not.toHaveBeenCalled();
});
it("should use the DB API call when there is a time range", () => {
const context: TimeRangeContextType = {
range: {
name: "test",
from: moment().subtract(1, "day"),
until: moment()
},
update: () => {}
};
const apiClient = ({
getClientsGraph: jest.fn(() => Promise.reject({ isCanceled: true })),
getClientsGraphDb: jest.fn(() => Promise.reject({ isCanceled: true }))
} as any) as ApiClient;
shallow(
<TimeRangeContext.Provider value={context}>
<ClientsGraphContainer apiClient={apiClient} />
</TimeRangeContext.Provider>
)
.dive()
.dive()
.dive();
expect(apiClient.getClientsGraph).not.toHaveBeenCalled();
expect(apiClient.getClientsGraphDb).toHaveBeenCalledWith(context.range, 600);
});
it("should transform the data and render if the API returns data", async () => {
const context: TimeRangeContextType = {
range: null,
update: () => {}
};
const apiClient = ({
getClientsGraph: () => Promise.resolve(fakeData)
} as any) as ApiClient;
const wrapper = shallow(
<TimeRangeContext.Provider value={context}>
<ClientsGraphContainer apiClient={apiClient} />
</TimeRangeContext.Provider>
)
.dive()
.dive()
.dive();
// Let the API call resolve
await tick();
wrapper.update();
const actualProps = wrapper.find(TranslatedClientsGraph).props();
const expectedProps = transformData(fakeData, null);
expect(actualProps).toEqual(expectedProps);
});
it("should render as loading if the API fails to return data", async () => {
const context: TimeRangeContextType = {
range: null,
update: () => {}
};
const apiClient = ({
getClientsGraph: () => Promise.reject({ error: {} })
} as any) as ApiClient;
const wrapper = shallow(
<TimeRangeContext.Provider value={context}>
<ClientsGraphContainer apiClient={apiClient} />
</TimeRangeContext.Provider>
)
.dive()
.dive()
.dive();
// Let the API call resolve
await tick();
wrapper.update();
const actualProps = wrapper.find(TranslatedClientsGraph).props();
expect(actualProps).toEqual(loadingProps);
});
+2 -2
View File
@@ -188,7 +188,7 @@ interface ApiTopDomains {
}
interface ApiTopClients {
top_clients: Array<ApiClient>;
top_clients: Array<ApiClientData>;
total_queries: number;
}
@@ -197,7 +197,7 @@ interface ApiTopBlockedClients {
blocked_queries: number;
}
interface ApiClient {
interface ApiClientData {
name: string;
ip: string;
count: number;