Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RTK Query example with Vitest configuration #3269

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions examples/query/react/vitest/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React Vitest example</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
31 changes: 31 additions & 0 deletions examples/query/react/vitest/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "vitest",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "vitest"
},
"dependencies": {
"@reduxjs/toolkit": "^1.9.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.5"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@vitejs/plugin-react": "^3.1.0",
"jsdom": "^21.1.1",
"msw": "^1.1.0",
"node-fetch": "^3.3.1",
"typescript": "^4.9.3",
"vite": "^4.1.0",
"vitest": "^0.29.2"
}
}
4 changes: 4 additions & 0 deletions examples/query/react/vitest/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.App {
font-family: sans-serif;
text-align: center;
}
42 changes: 42 additions & 0 deletions examples/query/react/vitest/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { rest } from "msw";
import { screen } from "@testing-library/react";

import App from "./App";
import { server } from "./test/server";
import { renderWithProviders } from "./test/test-utils";

describe("App", () => {
it("handles good response", async () => {
renderWithProviders(<App />);

screen.getByText("Loading...");

await screen.findByRole("heading", { name: /bulbasaur/i });

const img = screen.getByRole("img", {
name: /bulbasaur/i,
}) as HTMLImageElement;

expect(img.src).toBe(
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/1.png"
);
});

it("handles error response", async () => {
// force msw to return error response
server.use(
rest.get(
"https://pokeapi.co/api/v2/pokemon/bulbasaur",
(req, res, ctx) => {
return res(ctx.status(500));
}
)
);

renderWithProviders(<App />);

screen.getByText("Loading...");

await screen.findByText("Oh no, there was an error");
});
});
21 changes: 21 additions & 0 deletions examples/query/react/vitest/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import "./App.css";
import { useGetPokemonByNameQuery } from "./services/pokemon";

export default function App() {
const { data, error, isLoading } = useGetPokemonByNameQuery("bulbasaur");

return (
<div className="App">
{error ? (
<>Oh no, there was an error</>
) : isLoading ? (
<>Loading...</>
) : data ? (
<>
<h3>{data.species.name}</h3>
<img src={data.sprites.front_shiny} alt={data.species.name} />
</>
) : null}
</div>
);
}
15 changes: 15 additions & 0 deletions examples/query/react/vitest/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import App from "./App";
import { setupStore } from "./store";

const store = setupStore();

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
14 changes: 14 additions & 0 deletions examples/query/react/vitest/src/services/pokemon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

export const pokemonApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: "https://pokeapi.co/api/v2/" }),
tagTypes: [],
endpoints: (builder) => ({
getPokemonByName: builder.query({
query: (name: string) => `pokemon/${name}`,
}),
}),
});

// Export hooks for usage in functional components
export const { useGetPokemonByNameQuery } = pokemonApi;
17 changes: 17 additions & 0 deletions examples/query/react/vitest/src/setupTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import "@testing-library/jest-dom";

import fetch, { Headers, Request, Response } from "node-fetch";
import { server } from "./test/server";

// @ts-ignore
globalThis.fetch = fetch;
// @ts-ignore
globalThis.Headers = Headers;
// @ts-ignore
globalThis.Request = Request;
// @ts-ignore
globalThis.Response = Response;

beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
21 changes: 21 additions & 0 deletions examples/query/react/vitest/src/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import type { PreloadedState } from "@reduxjs/toolkit";
import { pokemonApi } from "./services/pokemon";

const rootReducer = combineReducers({
[pokemonApi.reducerPath]: pokemonApi.reducer,
});

export const setupStore = (preloadedState?: PreloadedState<RootState>) => {
return configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
// adding the api middleware enables caching, invalidation, polling and other features of `rtk-query`
getDefaultMiddleware().concat(pokemonApi.middleware),
preloadedState,
});
};

export type RootState = ReturnType<typeof rootReducer>;
export type AppStore = ReturnType<typeof setupStore>;
export type AppDispatch = AppStore["dispatch"];
6 changes: 6 additions & 0 deletions examples/query/react/vitest/src/test/server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { setupServer } from "msw/node";
import { handlers } from "./serverHandlers";

const server = setupServer(...handlers);

export { server };
18 changes: 18 additions & 0 deletions examples/query/react/vitest/src/test/server/serverHandlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { rest } from "msw";

const handlers = [
rest.get("https://pokeapi.co/api/v2/pokemon/bulbasaur", (req, res, ctx) => {
const mockApiResponse = {
species: {
name: "bulbasaur",
},
sprites: {
front_shiny:
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/1.png",
},
};
return res(ctx.json(mockApiResponse));
}),
];

export { handlers };
32 changes: 32 additions & 0 deletions examples/query/react/vitest/src/test/test-utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { render } from "@testing-library/react";
import type { RenderOptions } from "@testing-library/react";
import React, { PropsWithChildren } from "react";
import { Provider } from "react-redux";
import { setupStore } from "../store";
import type { AppStore, RootState } from "../store";
import type { PreloadedState } from "@reduxjs/toolkit";

// This type interface extends the default options for render from RTL, as well
// as allows the user to specify other things such as initialState, store. For
// future dependencies, such as wanting to test with react-router, you can extend
// this interface to accept a path and route and use those in a <MemoryRouter />
interface ExtendedRenderOptions extends Omit<RenderOptions, "queries"> {
preloadedState?: PreloadedState<RootState>;
store?: AppStore;
}

function renderWithProviders(
ui: React.ReactElement,
{
preloadedState = {},
store = setupStore(preloadedState),
...renderOptions
}: ExtendedRenderOptions = {}
) {
function Wrapper({ children }: PropsWithChildren<{}>): JSX.Element {
return <Provider store={store}>{children}</Provider>;
}
return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) };
}

export { renderWithProviders };
1 change: 1 addition & 0 deletions examples/query/react/vitest/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
21 changes: 21 additions & 0 deletions examples/query/react/vitest/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
9 changes: 9 additions & 0 deletions examples/query/react/vitest/tsconfig.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
14 changes: 14 additions & 0 deletions examples/query/react/vitest/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// <reference types="vitest" />

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: "jsdom",
setupFiles: ["src/setupTests.ts"],
},
});
Loading
Oops, something went wrong.