Skip to content

Commit

Permalink
Add tests for timeseries route
Browse files Browse the repository at this point in the history
  • Loading branch information
benvinegar committed Nov 18, 2024
1 parent aa995ea commit 1d13565
Show file tree
Hide file tree
Showing 5 changed files with 603 additions and 914 deletions.
2 changes: 1 addition & 1 deletion app/components/TimeSeriesChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export default function TimeSeriesChart({
}

return (
<ResponsiveContainer width="100%" height="100%">
<ResponsiveContainer width="100%" height="100%" minWidth={100}>
<AreaChart
width={500}
height={400}
Expand Down
3 changes: 1 addition & 2 deletions app/lib/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { getFiltersFromSearchParams } from "../utils";
import { getFiltersFromSearchParams, getDateTimeRange } from "../utils";
import { describe, test, expect, beforeEach, afterEach, vi } from "vitest";
import { getDateTimeRange } from "../utils";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
Expand Down
176 changes: 176 additions & 0 deletions app/routes/__tests__/resources.timeseries.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// @vitest-environment jsdom

import { ReactNode } from "react";
import { describe, test, expect, beforeEach, vi, afterEach } from "vitest";
import { render, waitFor, screen } from "@testing-library/react";
import { loader, TimeSeriesCard } from "../resources.timeseries";
import * as RemixReact from "@remix-run/react";
import "vitest-dom/extend-expect";
import { getDefaultContext } from "./testutils";

// Mock the useFetcher hook
vi.mock("@remix-run/react", async () => {
const actual = await vi.importActual("@remix-run/react");
return {
...actual,
useFetcher: vi.fn(),
};
});

describe("resources.timeseries loader", () => {
const { context } = getDefaultContext();

beforeEach(() => {
vi.spyOn(
context.analyticsEngine,
"getViewsGroupedByInterval",
).mockResolvedValue([
["2024-01-15T00:00:00Z", 100],
["2024-01-16T00:00:00Z", 200],
]);

// mock out responsive container to just return a standard div, otherwise
// recharts doesnt render underneath
vi.mock("recharts", async () => {
const OriginalModule = await vi.importActual("recharts");
return {
...OriginalModule,
ResponsiveContainer: ({
children,
}: {
children: ReactNode;
}) => <div>{children}</div>,
};
});
});

afterEach(() => {
vi.restoreAllMocks();
});

test("processes data correctly", async () => {
const request = new Request(
"http://test.com?interval=7d&site=test-site&timezone=UTC",
);
const result = await loader({
// @ts-expect-error we don't need to provide all the properties of the contextobject
context,
request,
});

const data = await result.json();
expect(data.chartData).toEqual([
{ date: "2024-01-15T00:00:00Z", views: 100 },
{ date: "2024-01-16T00:00:00Z", views: 200 },
]);
expect(data.intervalType).toBe("DAY");

expect(
context.analyticsEngine.getViewsGroupedByInterval,
).toHaveBeenCalledWith(
"test-site",
"DAY",
expect.any(Date),
expect.any(Date),
"UTC",
{},
);
});
});

describe("TimeSeriesCard", () => {
const mockFetcher = {
submit: vi.fn(),
data: {
chartData: [
{ date: "2024-01-15T00:00:00Z", views: 100 },
{ date: "2024-01-16T00:00:00Z", views: 200 },
],
intervalType: "DAY",
},
};

beforeEach(() => {
// @ts-expect-error we don't need to provide all the properties of the mockFetcher
vi.mocked(RemixReact.useFetcher).mockReturnValue(mockFetcher);

// Mock ResizeObserver for recharts
global.ResizeObserver = vi.fn().mockImplementation(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));
});

afterEach(() => {
vi.restoreAllMocks();
});

test("fetches data on mount", () => {
const props = {
siteId: "test-site",
interval: "7d",
filters: {},
timezone: "UTC",
};

render(<TimeSeriesCard {...props} />);

expect(mockFetcher.submit).toHaveBeenCalledWith(
{
site: "test-site",
interval: "7d",
timezone: "UTC",
},
{
method: "get",
action: "/resources/timeseries",
},
);
});

test("renders TimeSeriesChart when data is available", async () => {
const props = {
siteId: "test-site",
interval: "7d",
filters: {},
timezone: "UTC",
};

render(<TimeSeriesCard {...props} />);

// Wait for the chart to be rendered
await waitFor(() => screen.getAllByText("Mon, Jan 15").length > 0);
// assert data appears in chart
expect(screen.getAllByText("100")).toHaveLength(2);
});

test("refetches when props change", () => {
expect(mockFetcher.submit).toHaveBeenCalledTimes(0);

const props = {
siteId: "test-site",
interval: "7d",
filters: {},
timezone: "UTC",
};

const { rerender } = render(<TimeSeriesCard {...props} />);

// Change interval
rerender(<TimeSeriesCard {...props} interval="1d" />);

expect(mockFetcher.submit).toHaveBeenCalledTimes(2);
expect(mockFetcher.submit).toHaveBeenLastCalledWith(
{
site: "test-site",
interval: "1d",
timezone: "UTC",
},
{
method: "get",
action: "/resources/timeseries",
},
);
});
});
Loading

0 comments on commit 1d13565

Please sign in to comment.