Skip to content

Commit 934c5ca

Browse files
authored
Merge pull request #35 from sectsect/feature/testing-for-postcontent
test: add testing for component 'PostContent'
2 parents ccb800a + 081eca1 commit 934c5ca

3 files changed

Lines changed: 237 additions & 1 deletion

File tree

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import type { JSX } from 'solid-js';
2+
3+
import { MetaProvider } from '@solidjs/meta';
4+
import {
5+
Route,
6+
Router,
7+
MemoryRouter,
8+
createMemoryHistory,
9+
} from '@solidjs/router';
10+
import { render, renderHook, screen, waitFor } from '@solidjs/testing-library';
11+
import { QueryClient, QueryClientProvider } from '@tanstack/solid-query';
12+
import { HttpResponse, http } from 'msw';
13+
import toast from 'solid-toast';
14+
import { describe, expect, test, vi } from 'vitest';
15+
16+
import App from '@/App';
17+
import PostContent from '@/components/modules/PostContent/PostContent';
18+
import useFetchPost from '@/hooks/useFetchPost';
19+
import { server } from '@/mocks/server';
20+
21+
const apiEndpointUrl = import.meta.env.VITE_PUBLIC_API_URL;
22+
23+
vi.mock('solid-toast', () => ({
24+
default: {
25+
error: vi.fn(),
26+
},
27+
Toaster: vi.fn(),
28+
}));
29+
30+
describe('PostContent component', () => {
31+
test('should render "Loading..." element before Data Fetching', async () => {
32+
const queryClient = new QueryClient();
33+
34+
render(() => <PostContent />, {
35+
wrapper: props => (
36+
<MetaProvider>
37+
<QueryClientProvider client={queryClient}>
38+
<Router
39+
// eslint-disable-next-line @typescript-eslint/no-shadow
40+
root={props => <>{props.children}</>}
41+
>
42+
<Route path="/" component={() => <>{props.children}</>} />
43+
</Router>
44+
</QueryClientProvider>
45+
</MetaProvider>
46+
),
47+
});
48+
49+
// @ https://github.com/testing-library/react-hooks-testing-library/issues/23#issuecomment-477542354
50+
const wrapper = (props: { children: JSX.Element }) => (
51+
<QueryClientProvider client={queryClient}>
52+
<Router
53+
// eslint-disable-next-line @typescript-eslint/no-shadow
54+
root={props => <>{props.children}</>}
55+
>
56+
<Route path="/" component={() => <>{props.children}</>} />
57+
</Router>
58+
</QueryClientProvider>
59+
);
60+
61+
renderHook(() => useFetchPost('1'), { wrapper });
62+
63+
// screen.getByRole('button', { name: '' });
64+
const loadingEle = screen.getByText('Loading...'); // substring match
65+
expect(loadingEle).toBeInTheDocument();
66+
});
67+
68+
test('should success to fetch data', async () => {
69+
const queryClient = new QueryClient();
70+
71+
render(() => <PostContent />, {
72+
wrapper: props => (
73+
<MetaProvider>
74+
<QueryClientProvider client={queryClient}>
75+
<Router
76+
// eslint-disable-next-line @typescript-eslint/no-shadow
77+
root={props => <>{props.children}</>}
78+
>
79+
<Route path="/" component={() => <>{props.children}</>} />
80+
</Router>
81+
</QueryClientProvider>
82+
</MetaProvider>
83+
),
84+
});
85+
86+
// expect(await screen.findByText(/qui est esse/)).toBeInTheDocument();
87+
88+
// @ https://github.com/testing-library/react-hooks-testing-library/issues/23#issuecomment-477542354
89+
const wrapper = (props: { children: JSX.Element }) => (
90+
<QueryClientProvider client={queryClient}>
91+
<Router
92+
// eslint-disable-next-line @typescript-eslint/no-shadow
93+
root={props => <>{props.children}</>}
94+
>
95+
<Route path="/" component={() => <>{props.children}</>} />
96+
</Router>
97+
</QueryClientProvider>
98+
);
99+
100+
const { result } = renderHook(() => useFetchPost('1'), { wrapper });
101+
102+
// screen.getByRole('');
103+
104+
await waitFor(() => {
105+
expect(result.isSuccess).toBe(true);
106+
});
107+
108+
await waitFor(() => {
109+
expect(result.data?.id).toBe(1);
110+
});
111+
112+
await waitFor(async () => {
113+
expect(result.data?.title).toEqual(
114+
'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
115+
);
116+
});
117+
});
118+
119+
test('should render post title and body', async () => {
120+
// Mock window.ResizeObserver @ https://vitest.dev/guide/mocking.html#globals
121+
const ResizeObserverMock = vi.fn(() => ({
122+
disconnect: vi.fn(),
123+
observe: vi.fn(),
124+
takeRecords: vi.fn(),
125+
unobserve: vi.fn(),
126+
}));
127+
vi.stubGlobal('ResizeObserver', ResizeObserverMock);
128+
// now you can access it as `ResizeObserver` or `window.ResizeObserver`
129+
130+
const queryClient = new QueryClient();
131+
132+
// @see https://github.com/solidjs/solid-router/issues/335#issuecomment-1918115042
133+
const history = createMemoryHistory();
134+
history.set({ value: '/post/1' });
135+
136+
render(() => (
137+
<MetaProvider>
138+
<QueryClientProvider client={queryClient}>
139+
<MemoryRouter root={App} history={history}>
140+
<Route path="/post/:id" component={PostContent} />
141+
</MemoryRouter>
142+
</QueryClientProvider>
143+
</MetaProvider>
144+
));
145+
146+
await waitFor(async () => {
147+
expect(
148+
screen.getByText(
149+
'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
150+
),
151+
).toBeInTheDocument();
152+
});
153+
154+
await waitFor(async () => {
155+
expect(
156+
screen.getByText(
157+
'quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto',
158+
),
159+
).toBeInTheDocument();
160+
});
161+
});
162+
163+
test('should handle API call failure', async () => {
164+
const errorMessage = 'Failed to fetch data.';
165+
const toastErrorSpy = vi.spyOn(toast, 'error');
166+
167+
server.use(
168+
http.get(
169+
`${apiEndpointUrl}/posts/1`,
170+
() =>
171+
new HttpResponse(null, {
172+
status: 500,
173+
}),
174+
),
175+
);
176+
177+
const queryClient = new QueryClient({
178+
defaultOptions: { queries: { retry: false } }, // @ https://github.com/TanStack/query/discussions/2300#discussioncomment-811768
179+
});
180+
181+
render(() => <PostContent />, {
182+
wrapper: props => (
183+
<MetaProvider>
184+
<QueryClientProvider client={queryClient}>
185+
<Router
186+
// eslint-disable-next-line @typescript-eslint/no-shadow
187+
root={props => <>{props.children}</>}
188+
>
189+
<Route path="/" component={() => <>{props.children}</>} />
190+
</Router>
191+
</QueryClientProvider>
192+
</MetaProvider>
193+
),
194+
});
195+
196+
const wrapper = (props: { children: JSX.Element }) => (
197+
<QueryClientProvider client={queryClient}>
198+
<Router
199+
// eslint-disable-next-line @typescript-eslint/no-shadow
200+
root={props => <>{props.children}</>}
201+
>
202+
<Route
203+
path="/"
204+
component={() => (
205+
<>
206+
{props.children}
207+
{/* <Toaster position="bottom-center" /> */}
208+
</>
209+
)}
210+
/>
211+
</Router>
212+
</QueryClientProvider>
213+
);
214+
215+
// screen.getByRole('');
216+
const { result } = renderHook(() => useFetchPost('1'), {
217+
wrapper,
218+
});
219+
220+
await waitFor(() => expect(result.isError).toBe(true));
221+
222+
await waitFor(() => {
223+
expect(result.error).toEqual(Error('Failed to fetch data'));
224+
});
225+
226+
await waitFor(() => {
227+
expect(toastErrorSpy).toHaveBeenCalledWith(errorMessage);
228+
});
229+
230+
// expect(
231+
// await screen.findByText('Failed to fetch data.'),
232+
// ).toBeInTheDocument();
233+
});
234+
});

apps/solidjs-boilerplate/src/hooks/useFetchPost.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ const useFetchPost = (id: string) => {
2727
if (!result.ok) throw new Error('Failed to fetch data');
2828
return result.json();
2929
},
30-
3130
staleTime: 1000 * 60 * 5, // 5 minutes
3231
throwOnError: true, // Throw an error if the query fails
3332
}));

apps/solidjs-boilerplate/src/mocks/handlers.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@ export const handlers = [
1818
http.get(`${apiEndpointUrl}/posts`, () => {
1919
return HttpResponse.json(posts);
2020
}),
21+
http.get(`${apiEndpointUrl}/posts/1`, () => {
22+
return HttpResponse.json(posts[0]);
23+
}),
2124
];

0 commit comments

Comments
 (0)