From 37e9d81ca9db300bc865679355548631f10b8c07 Mon Sep 17 00:00:00 2001 From: SeboMyr <80338167+Seborider@users.noreply.github.com> Date: Sun, 29 Oct 2023 14:26:42 +0100 Subject: [PATCH] react missions --- .prettierignore | 1 + .prettierrc | 1 + package-lock.json | 18 +++++++ package.json | 3 ++ public/index.html | 2 +- src/App.test.tsx | 40 +++++++++++--- src/App.tsx | 32 +++++------ src/components/items-list/items-list.css | 23 ++++++++ src/components/items-list/items-list.test.tsx | 54 +++++++++++++++++++ src/components/items-list/items-list.tsx | 46 ++++++++++++++++ src/index.css | 6 +-- src/index.tsx | 16 +++--- src/services/items-api/items-api-service.ts | 14 +++++ src/setupTests.ts | 5 -- tsconfig.json | 10 +--- 15 files changed, 221 insertions(+), 50 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 src/components/items-list/items-list.css create mode 100644 src/components/items-list/items-list.test.tsx create mode 100644 src/components/items-list/items-list.tsx create mode 100644 src/services/items-api/items-api-service.ts delete mode 100644 src/setupTests.ts diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/package-lock.json b/package-lock.json index 3032566..2f9ebae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,9 @@ "react-scripts": "5.0.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "prettier": "3.0.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -13810,6 +13813,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", diff --git a/package.json b/package.json index 8f9a736..dcf3e06 100644 --- a/package.json +++ b/package.json @@ -39,5 +39,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "prettier": "3.0.3" } } diff --git a/public/index.html b/public/index.html index aa069f2..e65acb3 100644 --- a/public/index.html +++ b/public/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/App.test.tsx b/src/App.test.tsx index 6b51971..c84d720 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -1,10 +1,34 @@ -import React from "react" -import { render, screen } from "@testing-library/react" -import App from "./App" +import React from "react"; +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import App from "./App"; -test("renders learn react link", () => { - render() - const linkElement = screen.getByText(/Click here/i) - expect(linkElement).toBeInTheDocument() -}) +describe("App component", () => { + test("renders App with ItemsList component", async () => { + render(); + const button = screen.getByText(/click here/i); + expect(button).toBeInTheDocument(); + await waitFor(() => screen.getByText(/Times clicked/)); + }); + + test("clicking button in ItemsList increases count", async () => { + render(); + + const button = screen.getByText(/click here/i); + + expect(screen.getByText("0 Times clicked")).toBeInTheDocument(); + + await waitFor(() => { + fireEvent.click(button); + }); + + expect(screen.getByText("1 Times clicked")).toBeInTheDocument(); + + await waitFor(() => { + fireEvent.click(button); + }); + + expect(screen.getByText("2 Times clicked")).toBeInTheDocument(); + }); +}); diff --git a/src/App.tsx b/src/App.tsx index 5936e14..07f2c13 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,23 +1,19 @@ -import React, { useEffect, useState } from "react" -import "./App.css" +import React, { useState } from "react"; +import "./App.css"; +import ItemsList from "./components/items-list/items-list"; + +const App: React.FC = () => { + const [count, setCount] = useState(0); + + const handleIncrement = () => { + setCount((prevCount) => prevCount + 1); + }; -function App() { - const [message, setMessage] = useState("") - const [count, setCount] = useState(0) - useEffect(() => { - setMessage(`${count} Times clicked`) - }) - const onClick = () => { - setCount(count + 1) - } return (
- - Click here - -
{message}
+
- ) -} + ); +}; -export default App +export default App; diff --git a/src/components/items-list/items-list.css b/src/components/items-list/items-list.css new file mode 100644 index 0000000..3b73f7f --- /dev/null +++ b/src/components/items-list/items-list.css @@ -0,0 +1,23 @@ +.page-container { + display: flex; + flex-direction: column; + padding: 20px; +} + +.items-container { + margin: 20px; + padding: 20px; + border: 1px solid #cccccc; + border-radius: 4px; +} + +.items-container ul { + list-style-type: none; + padding: 0; +} + +.button-message-container { + display: flex; + gap: 1rem; + padding: 20px; +} diff --git a/src/components/items-list/items-list.test.tsx b/src/components/items-list/items-list.test.tsx new file mode 100644 index 0000000..831c460 --- /dev/null +++ b/src/components/items-list/items-list.test.tsx @@ -0,0 +1,54 @@ +jest.mock("../../services/items-api/items-api-service"); + +import React from "react"; +import { render, screen, fireEvent } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import ItemsList from "./items-list"; +import { fetchItems } from "../../services/items-api/items-api-service"; +import { act } from "react-dom/test-utils"; +describe("ItemsList component", () => { + let promise: Promise<{ id: string; label: string }[]>; + + beforeEach(() => { + jest.clearAllMocks(); + + promise = Promise.resolve([{ id: "1", label: "Item 1" }]); + + (fetchItems as jest.Mock).mockReturnValue(promise); + }); + + test("fetches and renders items", async () => { + act(() => { + render( {}} />); + }); + + await act(() => promise); + + expect(screen.getByText(/Item 1 with ID: 1/)).toBeInTheDocument(); + }); + + test("displays the correct click count message", async () => { + act(() => { + render( {}} />); + }); + + await act(() => promise); + + const message = screen.getByText("3 Times clicked"); + expect(message).toBeInTheDocument(); + }); + + test("button click calls onIncrement", async () => { + const handleIncrement = jest.fn(); + + act(() => { + render(); + }); + + await act(() => promise); + + fireEvent.click(screen.getByText("Click here")); + + expect(handleIncrement).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/components/items-list/items-list.tsx b/src/components/items-list/items-list.tsx new file mode 100644 index 0000000..91519b8 --- /dev/null +++ b/src/components/items-list/items-list.tsx @@ -0,0 +1,46 @@ +import React, { useEffect, useState } from "react"; +import { + fetchItems, + IListItem, +} from "../../services/items-api/items-api-service"; +import "./items-list.css"; + +interface ItemsListProps { + count: number; + onIncrement: () => void; +} + +const ItemsList: React.FC = ({ count, onIncrement }) => { + const [items, setItems] = useState([]); + const [message, setMessage] = useState(""); + + useEffect(() => { + setMessage(`${count} Times clicked`); + }, [count]); + + useEffect(() => { + fetchItems().then((data) => setItems(data)); + }, []); + + return ( +
+
+
    + {items.map((item) => ( +
  • + {item.label} with ID: {item.id} +
  • + ))} +
+
+
+ +
{message}
+
+
+ ); +}; + +export default ItemsList; diff --git a/src/index.css b/src/index.css index ec2585e..4a1df4d 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1,13 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } diff --git a/src/index.tsx b/src/index.tsx index f50b6dd..e36f828 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,11 +1,13 @@ -import React from "react" -import ReactDOM from "react-dom/client" -import "./index.css" -import App from "./App" +import React from "react"; +import ReactDOM from "react-dom/client"; +import "./index.css"; +import App from "./App"; -const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement) +const root = ReactDOM.createRoot( + document.getElementById("root") as HTMLElement, +); root.render( - -) + , +); diff --git a/src/services/items-api/items-api-service.ts b/src/services/items-api/items-api-service.ts new file mode 100644 index 0000000..f56abff --- /dev/null +++ b/src/services/items-api/items-api-service.ts @@ -0,0 +1,14 @@ +export interface IListItem { + id: number; + label: string; +} + +export const fetchItems = (): Promise => { + return Promise.resolve([ + { id: 1, label: "Some label 1" }, + { id: 2, label: "Some label 2" }, + { id: 3, label: "Some label 3" }, + { id: 4, label: "Some label 4" }, + { id: 5, label: "Some label 5" }, + ]); +}; diff --git a/src/setupTests.ts b/src/setupTests.ts deleted file mode 100644 index 8f2609b..0000000 --- a/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; diff --git a/tsconfig.json b/tsconfig.json index a273b0c..9d379a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -20,7 +16,5 @@ "noEmit": true, "jsx": "react-jsx" }, - "include": [ - "src" - ] + "include": ["src"] }