Skip to content

Commit 3dc783f

Browse files
fehmerMiodec
andauthored
impr: use tanstack/query on AboutPage (@fehmer) (#7466)
Co-authored-by: Jack <jack@monkeytype.com>
1 parent c59f121 commit 3dc783f

17 files changed

Lines changed: 911 additions & 631 deletions

File tree

frontend/.oxlintrc-plugin.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
},
88
"overrides": [
99
{
10-
"jsPlugins": ["eslint-plugin-solid"],
11-
"files": ["src/**/*.tsx"],
10+
"jsPlugins": ["eslint-plugin-solid", "@tanstack/eslint-plugin-query"],
11+
"files": ["src/**/*.tsx", "src/**/*.ts"],
1212
"rules": {
1313
"solid/components-return-once": "error",
1414
"solid/event-handlers": "error",
@@ -32,7 +32,14 @@
3232
"html": "void"
3333
}
3434
],
35-
"solid/style-prop": "error"
35+
"solid/style-prop": "error",
36+
"@tanstack/query/exhaustive-deps": "error",
37+
"@tanstack/query/no-rest-destructuring": "error",
38+
"@tanstack/query/stable-query-client": "error",
39+
"@tanstack/query/no-unstable-deps": "error",
40+
"@tanstack/query/infinite-query-property-order": "error",
41+
"@tanstack/query/no-void-query-fn": "error",
42+
"@tanstack/query/mutation-property-order": "error"
3643
}
3744
}
3845
]

frontend/__tests__/components/AsyncContent.spec.tsx

Lines changed: 0 additions & 79 deletions
This file was deleted.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { render, screen, waitFor } from "@solidjs/testing-library";
2+
import { createResource, Resource, Show } from "solid-js";
3+
import { beforeEach, describe, expect, it, vi } from "vitest";
4+
5+
import AsyncContent from "../../../src/ts/components/common/AsyncContent";
6+
import * as Notifications from "../../../src/ts/elements/notifications";
7+
8+
describe("AsyncContent", () => {
9+
const addNotificationMock = vi.spyOn(Notifications, "add");
10+
11+
beforeEach(() => {
12+
addNotificationMock.mockClear();
13+
});
14+
describe("with resource", () => {
15+
it("renders loading state while resource is pending", () => {
16+
const [resource] = createResource(async () => {
17+
await new Promise((resolve) => setTimeout(resolve, 100));
18+
return "data";
19+
});
20+
21+
const { container } = renderWithResource(resource);
22+
23+
const preloader = container.querySelector(".preloader");
24+
expect(preloader).toBeInTheDocument();
25+
expect(preloader).toHaveClass("preloader");
26+
expect(preloader?.querySelector("i")).toHaveClass(
27+
"fas",
28+
"fa-fw",
29+
"fa-spin",
30+
"fa-circle-notch",
31+
);
32+
});
33+
34+
it("renders data when resource resolves", async () => {
35+
const [resource] = createResource(async () => {
36+
return "Test Data";
37+
});
38+
39+
renderWithResource(resource);
40+
41+
await waitFor(() => {
42+
expect(screen.getByTestId("content")).toHaveTextContent("Test Data");
43+
});
44+
});
45+
46+
it("renders error message when resource fails", async () => {
47+
const [resource] = createResource(async () => {
48+
throw new Error("Test error");
49+
});
50+
51+
renderWithResource(resource, "Custom error message");
52+
53+
await waitFor(() => {
54+
expect(screen.getByText(/Custom error message/)).toBeInTheDocument();
55+
});
56+
expect(addNotificationMock).toHaveBeenCalledWith(
57+
"Custom error message: Test error",
58+
-1,
59+
);
60+
});
61+
62+
it("renders default error message when no custom message provided", async () => {
63+
const [resource] = createResource(async () => {
64+
throw new Error("Test error");
65+
});
66+
67+
renderWithResource(resource);
68+
69+
await waitFor(() => {
70+
expect(screen.getByText(/An error occurred/)).toBeInTheDocument();
71+
});
72+
expect(addNotificationMock).toHaveBeenCalledWith(
73+
"An error occurred: Test error",
74+
-1,
75+
);
76+
});
77+
78+
it("renders content while resource is pending if alwaysShowContent", () => {
79+
const [resource] = createResource(async () => {
80+
await new Promise((resolve) => setTimeout(resolve, 100));
81+
return "data";
82+
});
83+
84+
const { container } = renderWithResource(resource, undefined, true);
85+
86+
const preloader = container.querySelector(".preloader");
87+
expect(preloader).not.toBeInTheDocument();
88+
expect(container.querySelector("div")).toHaveTextContent("no data");
89+
});
90+
91+
it("renders data when resource resolves if alwaysShowContent", async () => {
92+
const [resource] = createResource(async () => {
93+
return "Test Data";
94+
});
95+
96+
const { container } = renderWithResource(resource, undefined, true);
97+
98+
await waitFor(() => {
99+
expect(screen.getByTestId("content")).toHaveTextContent("Test Data");
100+
});
101+
const preloader = container.querySelector(".preloader");
102+
expect(preloader).not.toBeInTheDocument();
103+
});
104+
105+
it("renders error message when resource fails if alwaysShowContent", async () => {
106+
const [resource] = createResource(async () => {
107+
throw new Error("Test error");
108+
});
109+
110+
const { container } = renderWithResource(
111+
resource,
112+
"Custom error message",
113+
true,
114+
);
115+
116+
await waitFor(() => {
117+
expect(screen.getByText(/Custom error message/)).toBeInTheDocument();
118+
});
119+
expect(addNotificationMock).toHaveBeenCalledWith(
120+
"Custom error message: Test error",
121+
-1,
122+
);
123+
console.log(container.innerHTML);
124+
});
125+
function renderWithResource<T>(
126+
resource: Resource<T>,
127+
errorMessage?: string,
128+
alwaysShowContent?: true,
129+
): {
130+
container: HTMLElement;
131+
} {
132+
const { container } = render(() => (
133+
<AsyncContent
134+
resource={resource}
135+
errorMessage={errorMessage}
136+
alwaysShowContent={alwaysShowContent}
137+
>
138+
{(data: T | undefined) => (
139+
<>
140+
foo
141+
<Show when={data !== undefined} fallback={<div>no data</div>}>
142+
<div data-testid="content">{String(data)}</div>
143+
</Show>
144+
</>
145+
)}
146+
</AsyncContent>
147+
));
148+
149+
return {
150+
container,
151+
};
152+
}
153+
});
154+
});

frontend/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
"@sentry/browser": "9.14.0",
3131
"@sentry/vite-plugin": "3.3.1",
3232
"@solidjs/meta": "0.29.4",
33+
"@tanstack/solid-query": "5.90.23",
34+
"@tanstack/solid-query-devtools": "5.91.3",
3335
"@tanstack/solid-table": "8.21.3",
3436
"@ts-rest/core": "3.52.1",
3537
"animejs": "4.2.2",
@@ -66,6 +68,7 @@
6668
"@monkeytype/typescript-config": "workspace:*",
6769
"@solidjs/testing-library": "0.8.10",
6870
"@tailwindcss/vite": "4.1.18",
71+
"@tanstack/eslint-plugin-query": "5.91.4",
6972
"@testing-library/dom": "10.4.1",
7073
"@testing-library/jest-dom": "6.9.1",
7174
"@testing-library/user-event": "14.6.1",

frontend/src/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,6 @@
7272

7373
<link rel="stylesheet" href="" id="globalFunBoxTheme" type="text/css" />
7474
<script type="module" src="ts/index.ts"></script>
75+
<mount data-component="devtools"></mount>
7576
</body>
7677
</html>

0 commit comments

Comments
 (0)