mirror of
https://github.com/gosticks/web.git
synced 2026-08-12 20:20:25 +00:00
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:
@@ -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>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
Vendored
+2
-2
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user