Skip to content

Commit 2545c37

Browse files
Add tests
1 parent f36a992 commit 2545c37

File tree

14 files changed

+438
-97
lines changed

14 files changed

+438
-97
lines changed

src/types/plt.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Axis } from "./axis";
2+
import { Color } from "./color";
3+
import { Font } from "./font";
4+
import { Plt } from "./plt";
5+
import { Trace } from "./trace";
6+
7+
describe("Plt", () => {
8+
it("constructs the plt with values", (): void => {
9+
const testValues = {
10+
title: "Testing",
11+
axes: [new Axis(), new Axis({ color: Color.RED })],
12+
pvlist: [new Trace({ yPv: "TEST" })],
13+
background: Color.WHITE,
14+
foreground: Color.RED,
15+
scroll: false,
16+
grid: true,
17+
scrollStep: 5,
18+
updatePeriod: 10,
19+
titleFont: new Font(14),
20+
labelFont: new Font(8),
21+
legendFont: new Font(8),
22+
scaleFont: new Font(6),
23+
start: "10 minutes",
24+
end: "now"
25+
};
26+
const plt = new Plt(testValues);
27+
const actualValues = {
28+
title: "Testing",
29+
axes: [new Axis(), new Axis({ color: Color.RED })],
30+
pvlist: [new Trace({ yPv: "TEST" })],
31+
backgroundColor: Color.WHITE,
32+
foregroundColor: Color.RED,
33+
scroll: false,
34+
showGrid: true,
35+
scrollStep: 5,
36+
updatePeriod: 10,
37+
titleFont: new Font(14),
38+
labelFont: new Font(8),
39+
legendFont: new Font(8),
40+
scaleFont: new Font(6),
41+
start: "10 minutes",
42+
end: "now"
43+
};
44+
expect(plt).toEqual(actualValues);
45+
expect(plt).toBeInstanceOf(Plt);
46+
});
47+
48+
it("construct the trace with only defaults", (): void => {
49+
const plt = new Plt();
50+
expect(plt).toEqual({
51+
axes: [new Axis()],
52+
pvlist: [new Trace()],
53+
title: "",
54+
backgroundColor: Color.WHITE,
55+
foregroundColor: Color.BLACK,
56+
scroll: true,
57+
showGrid: false,
58+
scrollStep: 5,
59+
updatePeriod: 1,
60+
titleFont: new Font(),
61+
labelFont: new Font(),
62+
legendFont: new Font(),
63+
scaleFont: new Font(),
64+
start: "1 minute",
65+
end: "now"
66+
});
67+
expect(plt).toBeInstanceOf(Plt);
68+
});
69+
});

src/types/plt.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Axes } from "./axis";
1+
import { Axes, Axis } from "./axis";
22
import { Color } from "./color";
33
import { Font } from "./font";
44
import { Trace } from "./trace";
@@ -26,8 +26,8 @@ export class Plt {
2626
*/
2727
public constructor({
2828
title = "",
29-
axes = [],
30-
pvlist = [],
29+
axes = [new Axis()],
30+
pvlist = [new Trace()],
3131
background = Color.WHITE,
3232
foreground = Color.BLACK,
3333
scroll = true,

src/types/trace.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ describe("Trace", () => {
1414
pointSize: 10,
1515
visible: false,
1616
xPv: "TEST-01:PV",
17-
yPv: "TEST-02:PV"
17+
yPv: "TEST-02:PV",
18+
archive: {
19+
name: "Test",
20+
url: "Testing.diamond.ac.uk"
21+
}
1822
};
1923
const trace = new Trace(testValues);
2024

@@ -34,7 +38,11 @@ describe("Trace", () => {
3438
pointType: 0,
3539
pointSize: 1,
3640
visible: true,
37-
yPv: ""
41+
yPv: "",
42+
archive: {
43+
name: "",
44+
url: ""
45+
}
3846
});
3947
expect(trace).toBeInstanceOf(Trace);
4048
});

src/types/trace.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export class Trace {
4444
updateDelay = 100,
4545
updateMode = 0,
4646
plotMode = 0,
47-
archive = undefined
47+
archive = { name: "", url: "" }
4848
} = {}) {
4949
// xPV property only exists on XYPlot
5050
if (xPv) this.xPv = xPv;
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import React from "react";
2+
import { act, render, screen } from "@testing-library/react";
3+
import { describe, test, expect, beforeEach, vi } from "vitest";
4+
import { Color, DType } from "../../../types";
5+
import { DataBrowserComponent } from "./dataBrowser";
6+
import { Trace } from "../../../types/trace";
7+
import { Axis } from "../../../types/axis";
8+
import { Plt } from "../../../types/plt";
9+
10+
declare global {
11+
// eslint-disable-next-line @typescript-eslint/no-namespace
12+
namespace NodeJS {
13+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
14+
interface Global {}
15+
}
16+
}
17+
18+
interface GlobalFetch extends NodeJS.Global {
19+
fetch: any;
20+
}
21+
const globalWithFetch = global as GlobalFetch;
22+
23+
// Mock the MUI X-Charts components
24+
vi.mock("@mui/x-charts", () => ({
25+
LineChart: vi.fn(({ series, xAxis, yAxis, sx }) => (
26+
<div
27+
data-testid="line-chart"
28+
data-series={JSON.stringify(series)}
29+
data-xaxis={JSON.stringify(xAxis)}
30+
data-yaxis={JSON.stringify(yAxis)}
31+
style={sx}
32+
/>
33+
))
34+
}));
35+
36+
vi.mock("@mui/material", () => ({
37+
Box: vi.fn(({ children }) => <div data-testid="mui-box">{children}</div>),
38+
Typography: vi.fn(({ children }) => (
39+
<div data-testid="mui-typography">{children}</div>
40+
))
41+
}));
42+
43+
describe("DataBrowserComponent", () => {
44+
// Basic test setup
45+
const defaultProps = {
46+
value: {
47+
getDoubleValue: () => 50,
48+
getTime: () => {
49+
new Date(Date.now());
50+
}
51+
} as Partial<DType> as DType,
52+
connected: true,
53+
readonly: true,
54+
pvName: "TEST:PV",
55+
plt: new Plt({
56+
pvlist: [
57+
new Trace({
58+
archive: {
59+
name: "Primary",
60+
url: "http://archiver.diamond.ac.uk/retrieval"
61+
},
62+
yPv: "TEST:PV"
63+
})
64+
],
65+
axes: [new Axis()]
66+
})
67+
};
68+
69+
const mockSuccessResponse: any = JSON.stringify([
70+
{
71+
secs: new Date().getTime() - 250000,
72+
val: 52
73+
},
74+
{
75+
secs: new Date().getTime() - 200000,
76+
val: 45
77+
},
78+
{
79+
secs: new Date().getTime() - 70000,
80+
val: 60
81+
}
82+
]);
83+
console.log(mockSuccessResponse);
84+
const mockJsonPromise = Promise.resolve(
85+
JSON.parse(`[{"data": ${mockSuccessResponse}}]`)
86+
);
87+
const mockFetchPromise = Promise.resolve({
88+
json: (): Promise<unknown> => mockJsonPromise
89+
});
90+
const mockFetch = (): Promise<unknown> => mockFetchPromise;
91+
vi.spyOn(globalWithFetch, "fetch").mockImplementation(mockFetch);
92+
93+
beforeEach(() => {
94+
vi.clearAllMocks();
95+
});
96+
97+
describe("Rendering", () => {
98+
test("renders with default props", () => {
99+
render(<DataBrowserComponent {...defaultProps} />);
100+
101+
const lineChart = screen.getByTestId("line-chart");
102+
expect(lineChart).toBeDefined();
103+
104+
const yAxisData = JSON.parse(lineChart.getAttribute("data-yaxis") ?? "");
105+
const xAxisData = JSON.parse(lineChart.getAttribute("data-xaxis") ?? "");
106+
expect(yAxisData[0].position).toBe("left");
107+
expect(xAxisData[0].scaleType).toBe("time");
108+
});
109+
110+
test("renders with 1 y axis on either side", () => {
111+
const axes = [
112+
new Axis({ color: Color.RED }),
113+
new Axis({ color: Color.BLUE, onRight: true })
114+
];
115+
const newProps = {
116+
...defaultProps,
117+
plt: new Plt({ axes: axes })
118+
};
119+
render(<DataBrowserComponent {...newProps} />);
120+
121+
const lineChart = screen.getByTestId("line-chart");
122+
const yAxisData = JSON.parse(lineChart.getAttribute("data-yaxis") ?? "");
123+
124+
expect(yAxisData[0].color).toBe(Color.RED.toString());
125+
expect(yAxisData[1].color).toBe(Color.BLUE.toString());
126+
expect(yAxisData[1].position).toBe("right");
127+
});
128+
129+
test("renders with 5 minute archived data", () => {
130+
const newProps = {
131+
...defaultProps,
132+
plt: new Plt({ start: "5 min", end: "now" })
133+
};
134+
act(() => {
135+
render(<DataBrowserComponent {...newProps} />);
136+
});
137+
const lineChart = screen.getByTestId("line-chart");
138+
const xAxisData = JSON.parse(lineChart.getAttribute("data-xaxis") ?? "");
139+
140+
const expectedDiff = 300000; // 5 * 60 * 1000
141+
const actualDiff =
142+
new Date(xAxisData[0].max).getTime() -
143+
new Date(xAxisData[0].min).getTime();
144+
145+
const seriesData = JSON.parse(
146+
lineChart.getAttribute("data-series") ?? ""
147+
);
148+
149+
expect(actualDiff).toBe(expectedDiff);
150+
//expect(seriesData[0].data).toBe([52, 45, 60, 50]);
151+
});
152+
});
153+
});

src/ui/widgets/DataBrowser/dataBrowser.tsx

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -40,43 +40,42 @@ export const DataBrowserComponent = (
4040
useEffect(() => {
4141
// Runs whenever pvlist updated and fetches archiver data
4242
const fetchArchivedPVData = async () => {
43-
{
44-
// TO DO - use when multiple PVs enabled
45-
// plt.pvlist.forEach((trace) => {
46-
// //Make call to getPvsData for multiple pvs
47-
// })
48-
try {
49-
// Fetch archiver data for period
50-
const startTime = convertStringTimePeriod(plt.start);
51-
const endTime = convertStringTimePeriod(plt.end);
52-
const min = new Date(new Date().getTime() - startTime);
53-
const max = new Date(new Date().getTime() - endTime);
54-
const archiverCall = `${plt.pvlist[0].archive?.url}/data/getData.json?pv=${plt.pvlist[0].yPv}&from=${min.toISOString()}&to=${max.toISOString()}`;
55-
const resp = await fetch(archiverCall);
56-
const json = await resp.json();
43+
// TO DO - use when multiple PVs enabled
44+
// plt.pvlist.forEach((trace) => {
45+
// //Make call to getPvsData for multiple pvs
46+
// })
47+
try {
48+
// Fetch archiver data for period
49+
const startTime = convertStringTimePeriod(plt.start);
50+
const endTime = convertStringTimePeriod(plt.end);
51+
const min = new Date(new Date().getTime() - startTime);
52+
const max = new Date(new Date().getTime() - endTime);
53+
const archiverCall = `${plt.pvlist[0].archive?.url}/data/getData.json?pv=${plt.pvlist[0].yPv}&from=${min.toISOString()}&to=${max.toISOString()}`;
54+
const resp = await fetch(archiverCall);
55+
const json = await resp.json();
5756

58-
setData({
59-
x: json[0].data.map((item: any) => {
60-
return new Date(item.secs * 1000);
61-
}),
62-
y: json[0].data.map((item: any) => {
63-
return item.val;
64-
}),
65-
min: min,
66-
max: max
67-
});
68-
setArchiveDataLoaded(true);
69-
} catch (e) {
70-
log.error(
71-
`Failed to fetch archiver data for PV ${plt.pvlist[0].yPv} from ${plt.pvlist[0].archive?.url}.`
72-
);
73-
}
57+
setData({
58+
x: json[0].data.map((item: any) => {
59+
return new Date(item.secs * 1000);
60+
}),
61+
y: json[0].data.map((item: any) => {
62+
return item.val;
63+
}),
64+
min: min,
65+
max: max
66+
});
67+
setArchiveDataLoaded(true);
68+
} catch (e) {
69+
log.error(
70+
`Failed to fetch archiver data for PV ${plt.pvlist[0].yPv} from ${plt.pvlist[0].archive?.url}.`
71+
);
7472
}
7573
};
76-
fetchArchivedPVData();
77-
}, []);
74+
// Only fetch onces
75+
if (!archiveDataLoaded) fetchArchivedPVData();
76+
}, [archiveDataLoaded, plt.start, plt.end, plt.pvlist]);
7877

79-
return archiveDataLoaded ? (
78+
return (
8079
<StripChartComponent
8180
{...plt}
8281
traces={plt.pvlist}
@@ -85,8 +84,6 @@ export const DataBrowserComponent = (
8584
connected={props.connected}
8685
archivedData={data}
8786
/>
88-
) : (
89-
<></>
9087
);
9188
};
9289

0 commit comments

Comments
 (0)