implemented jest tests
This commit is contained in:
parent
acca5c387f
commit
f1b3d9e5be
32
README.md
32
README.md
|
@ -5,11 +5,18 @@ It contains:
|
||||||
- Navbar
|
- Navbar
|
||||||
|
|
||||||
- A page to search for movies by title:
|
- A page to search for movies by title:
|
||||||
|
- searchParams to string
|
||||||
|
- Object.entries(searchParams)
|
||||||
|
.map(
|
||||||
|
([key, value], i, arr) =>
|
||||||
|
`${key}=${value}${i < arr.length - 1 ? "&" : ""}`
|
||||||
|
)
|
||||||
|
.toString();
|
||||||
|
|
||||||
- SearchBar component:
|
- SearchBar component:
|
||||||
- Input for Title (Year & Plot too?)
|
- Input for Title
|
||||||
- Submit button
|
- Submit button
|
||||||
- Cear button
|
- Clear button
|
||||||
- List of results - paginated so we might need a pagination component:
|
- List of results - paginated so we might need a pagination component:
|
||||||
- Title
|
- Title
|
||||||
- Year
|
- Year
|
||||||
|
@ -50,7 +57,7 @@ External API Docs: https://www.omdbapi.com/
|
||||||
|
|
||||||
Built on:
|
Built on:
|
||||||
Next.js
|
Next.js
|
||||||
Server side rendering for list & movie details to hide api token - Could possibly use Route Handler
|
Server side rendering for list & movie details to hide api token/network requests and eliminate the need for state-management on data returned from the external API.
|
||||||
|
|
||||||
Switched to Tailwindcss for styling. Was using chakra ui components but it isn't server-side render friendly
|
Switched to Tailwindcss for styling. Was using chakra ui components but it isn't server-side render friendly
|
||||||
|
|
||||||
|
@ -60,18 +67,13 @@ Since we're using server side rendering for pages that fetch data, we don't need
|
||||||
We can implement Context in a client component for managing "Favorites" for a user if we'd like
|
We can implement Context in a client component for managing "Favorites" for a user if we'd like
|
||||||
this might look like:
|
this might look like:
|
||||||
|
|
||||||
favorites/page.tsx
|
- FavoriteContext - favorites, setFavorites
|
||||||
|
|
||||||
cosnt FavoriteContext = createContext({
|
- FavoriteProvider - A wrapper for FavoriteContext.Provider with local state that we can render client-side and pass children through to make use of server-side rendering for child components (interleaving)
|
||||||
favorites,
|
|
||||||
addFavorite
|
|
||||||
})
|
|
||||||
|
|
||||||
const FavoriteProvider = ()=>{
|
- FavoriteBtn - a client-side component rendering a button and using useContext(FormContext) to consume favorites and setFavorites - filter favorites by id to prevent duplicats/allow removing from list.
|
||||||
// local state management
|
|
||||||
return (
|
|
||||||
|
|
||||||
)
|
Known issues:
|
||||||
}
|
* https://github.com/vercel/next.js/issues/65161
|
||||||
|
* https://github.com/vercel/next.js/issues/54757
|
||||||
const { searchResults } = useContext(FavoriteContext);
|
|
2
movie-search/.jest/setEnvVars.js
Normal file
2
movie-search/.jest/setEnvVars.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
process.env.API_KEY='6200aa0'
|
||||||
|
process.env.OMDB_URL='http://www.omdbapi.com/'
|
16
movie-search/__tests__/MovieCard.test.jsx
Normal file
16
movie-search/__tests__/MovieCard.test.jsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import "@testing-library/jest-dom";
|
||||||
|
import {render, screen} from '@testing-library/react'
|
||||||
|
import MovieCard from '../src/app/Search/MovieCard.tsx'
|
||||||
|
const movie = {
|
||||||
|
Title: "Dune",
|
||||||
|
Year: "2000",
|
||||||
|
imdbID: "tt0142032",
|
||||||
|
Type: "series",
|
||||||
|
Poster: "https://m.media-amazon.com/images/M/MV5BMTU4MjMyMTkxN15BMl5BanBnXkFtZTYwODA5OTU5._V1_SX300.jpg"
|
||||||
|
}
|
||||||
|
|
||||||
|
test('MovieCard Renders',()=>{
|
||||||
|
render(<MovieCard movie={movie} />)
|
||||||
|
const movieTitle = screen.getByRole('heading')
|
||||||
|
expect(movieTitle).toHaveTextContent('Dune')
|
||||||
|
})
|
39
movie-search/__tests__/SearchBar.test.jsx
Normal file
39
movie-search/__tests__/SearchBar.test.jsx
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import "@testing-library/jest-dom";
|
||||||
|
import { fireEvent, render, screen } from "@testing-library/react";
|
||||||
|
import SearchBar from "@/app/Search/SearchBar.tsx";
|
||||||
|
import { queryOMDb } from "@/api/omdb";
|
||||||
|
|
||||||
|
test("SearchBar Allows user input", () => {
|
||||||
|
render(<SearchBar />);
|
||||||
|
const input = screen.getByPlaceholderText("Search by Title");
|
||||||
|
|
||||||
|
fireEvent.change(input, { target: { value: "some value" } });
|
||||||
|
|
||||||
|
expect(input.value).toBe("some value");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("SearchBar Allows user input and Clear button clears", () => {
|
||||||
|
render(<SearchBar />);
|
||||||
|
const input = screen.getByPlaceholderText("Search by Title");
|
||||||
|
const clear = screen.getByText("Clear");
|
||||||
|
|
||||||
|
fireEvent.change(input, { target: { value: "some value" } });
|
||||||
|
expect(input.value).toBe("some value");
|
||||||
|
|
||||||
|
fireEvent.click(clear);
|
||||||
|
|
||||||
|
expect(input.value).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
// test("SearchBar Allows user submit", () => {
|
||||||
|
// render(<SearchBar />);
|
||||||
|
// const input = screen.getByPlaceholderText("Search by Title");
|
||||||
|
// const submit = screen.getByText("Search");
|
||||||
|
// const spy = jest.spyOn({ queryOMDb }, "queryOMDb");
|
||||||
|
|
||||||
|
// fireEvent.change(input, { target: { value: "back to the" } });
|
||||||
|
// fireEvent.click(submit);
|
||||||
|
// // Somehow test for form submission - currently facing this issue
|
||||||
|
// // https://github.com/vercel/next.js/issues/54757
|
||||||
|
// // expect(spy).toHaveBeenCalled();
|
||||||
|
// });
|
8
movie-search/__tests__/omdb.test.jsx
Normal file
8
movie-search/__tests__/omdb.test.jsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import "@testing-library/jest-dom";
|
||||||
|
import { queryOMDb } from "../src/api/omdb.ts";
|
||||||
|
|
||||||
|
test("queryOMDb returns movie data", async () => {
|
||||||
|
const data = await queryOMDb("s=back to");
|
||||||
|
|
||||||
|
expect(data).toHaveProperty("Response");
|
||||||
|
});
|
19
movie-search/jest.config.ts
Normal file
19
movie-search/jest.config.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import type { Config } from 'jest'
|
||||||
|
import nextJest from 'next/jest.js'
|
||||||
|
|
||||||
|
const createJestConfig = nextJest({
|
||||||
|
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
|
||||||
|
dir: './',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add any custom config to be passed to Jest
|
||||||
|
const config: Config = {
|
||||||
|
coverageProvider: 'v8',
|
||||||
|
testEnvironment: 'jsdom',
|
||||||
|
setupFilesAfterEnv: ["<rootDir>/.jest/setEnvVars.js", "jest-fetch-mock"]
|
||||||
|
// Add more setup options before each test is run
|
||||||
|
// setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
|
||||||
|
}
|
||||||
|
|
||||||
|
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
|
||||||
|
export default createJestConfig(config)
|
7406
movie-search/package-lock.json
generated
7406
movie-search/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -6,7 +6,9 @@
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.11.4",
|
||||||
|
@ -14,15 +16,21 @@
|
||||||
"framer-motion": "^11.2.6",
|
"framer-motion": "^11.2.6",
|
||||||
"next": "14.2.3",
|
"next": "14.2.3",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18"
|
"react-dom": "^18",
|
||||||
|
"ts-node-dev": "^2.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@testing-library/jest-dom": "^6.4.5",
|
||||||
|
"@testing-library/react": "^15.0.7",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "14.2.3",
|
"eslint-config-next": "14.2.3",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"jest-fetch-mock": "^3.0.3",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
const url = `${process.env.OMDB_URL}?apikey=${process.env.API_KEY}`;
|
const url = `${process.env.OMDB_URL}?apikey=${process.env.API_KEY}`;
|
||||||
|
|
||||||
export const queryOMDb = async (queryParamString: string) => {
|
export const queryOMDb = async (queryParamString: string) => {
|
||||||
"use server";
|
"use server";
|
||||||
|
try {
|
||||||
const queryUrl = `${url}&${queryParamString}`;
|
const queryUrl = `${url}&${queryParamString}`;
|
||||||
const response = await fetch(queryUrl);
|
const response = await fetch(queryUrl);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Failed to fetch movie data");
|
throw new Error("Failed to fetch movie data");
|
||||||
}
|
}
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
console.log(queryUrl, result);
|
// console.log(queryUrl, result);
|
||||||
return result;
|
return result;
|
||||||
};
|
} catch (e) {
|
||||||
|
throw new Error("Something went wrong searching OMDb");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import FavoriteBtn from "@/components/FavoriteBtn";
|
import FavoriteBtn from "@/components/FavoriteBtn";
|
||||||
import ImgCard from "@/components/ImgCard";
|
import ImgCard from "@/components/ImgCard";
|
||||||
import Image from "next/image";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
export interface IMovieCardItem {
|
export interface IMovieCardItem {
|
||||||
|
@ -14,20 +13,6 @@ interface IMovieCardProps {
|
||||||
movie: IMovieCardItem;
|
movie: IMovieCardItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
const testclass = {
|
|
||||||
boxShadow: "md",
|
|
||||||
_hover: {
|
|
||||||
cursor: "pointer",
|
|
||||||
border: "1px solid blue",
|
|
||||||
borderRadius: 4,
|
|
||||||
// boxShadow:'border'
|
|
||||||
},
|
|
||||||
maxW: "sm",
|
|
||||||
borderWidth: "1px",
|
|
||||||
borderRadius: "lg",
|
|
||||||
overflow: "hidden",
|
|
||||||
}.toString();
|
|
||||||
|
|
||||||
export default ({ movie }: IMovieCardProps) => {
|
export default ({ movie }: IMovieCardProps) => {
|
||||||
return (
|
return (
|
||||||
<ImgCard
|
<ImgCard
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const stringifyQueryParams = (searchParams: object) =>
|
||||||
|
|
||||||
export default async ({ searchParams }: INextJsProps) => {
|
export default async ({ searchParams }: INextJsProps) => {
|
||||||
const queryParamString = stringifyQueryParams(searchParams!);
|
const queryParamString = stringifyQueryParams(searchParams!);
|
||||||
console.log(queryParamString)
|
// console.log(queryParamString)
|
||||||
const { Search } = await queryOMDb(queryParamString);
|
const { Search } = await queryOMDb(queryParamString);
|
||||||
const movies = Search || [];
|
const movies = Search || [];
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ export default ({ movie }: any) => {
|
||||||
if (isFavorite) {
|
if (isFavorite) {
|
||||||
setFavorites([...filteredFavorites])
|
setFavorites([...filteredFavorites])
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
setFavorites([...favorites, movie]);
|
setFavorites([...favorites, movie]);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -3,15 +3,11 @@ import Link from "next/link";
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
<nav className="px-2 flex gap-4">
|
<nav className="px-2 flex gap-4">
|
||||||
|
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<strong>Movie Search</strong>
|
<strong>Home</strong>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link href="/Search">Search</Link>
|
||||||
<div className="flex gap-4">
|
<Link href="/Favorites">Favorites</Link>
|
||||||
<Link href="/Search">Search</Link>
|
|
||||||
<Link href="/Favorites">Favorites</Link>
|
|
||||||
</div>
|
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,6 @@
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "__tests__/*.jsx"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user