diff --git a/package-lock.json b/package-lock.json index ecea5b7..2245bb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,13 +61,14 @@ "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -177,17 +178,19 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -201,23 +204,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", - "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "license": "MIT", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.9" + "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -255,25 +260,24 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", - "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", "dev": true, - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -297,12 +301,13 @@ } }, "node_modules/@babel/types": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", + "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2068,6 +2073,20 @@ "node": ">=12.0.0" } }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -2683,6 +2702,18 @@ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -2806,12 +2837,6 @@ "node": ">=8" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true - }, "node_modules/regexparam": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-2.0.2.tgz", @@ -3000,6 +3025,22 @@ "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", "dev": true }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -3118,13 +3159,17 @@ } }, "node_modules/vite": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz", - "integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" diff --git a/src/__tests__/app.test.jsx b/src/__tests__/app.test.jsx index e69de29..36ced63 100644 --- a/src/__tests__/app.test.jsx +++ b/src/__tests__/app.test.jsx @@ -0,0 +1,2 @@ +import './test_suites/DisplayUsers.test' +import './test_suites/ChangeStatus.test' \ No newline at end of file diff --git a/src/__tests__/setup.jsx b/src/__tests__/setup.jsx index e69de29..4250773 100644 --- a/src/__tests__/setup.jsx +++ b/src/__tests__/setup.jsx @@ -0,0 +1,51 @@ +import { afterEach } from 'vitest' +import { cleanup } from '@testing-library/react' +import '@testing-library/jest-dom/vitest' +import fetch from 'node-fetch'; + +global.fetch = fetch + +global.baseUsers = [ + { + "id": 1, + "name": "theGrey", + "profile_image": "./images/theGrey.jpg", + "status": "Online" + }, + { + "id": 2, + "name": "fr0d0", + "profile_image": "./images/fr0d0.jpg", + "status": "Away" + }, + { + "id": 3, + "name": "bor0mir123", + "profile_image": "./images/bor0mir123.jpg", + "status": "Offline" + }, + { + "id": 4, + "name": "elf-friend", + "profile_image": "./images/elf-friend.jpg", + "status": "Online" + }, + { + "id": 5, + "name": "legolas", + "profile_image": "./images/legolas.jpg", + "status": "Online" + } +] + +global.setFetchResponse = (val) => { + global.fetch = vi.fn(() => Promise.resolve({ + json: () => Promise.resolve(val), + ok: true, + status: 200 + })) +} + +afterEach(() => { + cleanup(); +}) \ No newline at end of file diff --git a/src/__tests__/test_suites/ChangeStatus.test.jsx b/src/__tests__/test_suites/ChangeStatus.test.jsx index e69de29..c00f0e4 100644 --- a/src/__tests__/test_suites/ChangeStatus.test.jsx +++ b/src/__tests__/test_suites/ChangeStatus.test.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; +import App from '../../components/App'; +import '@testing-library/jest-dom'; + +describe('Our App will', () => { + test('edit the status of the user', async () => { + global.setFetchResponse(global.baseUsers) + const { findAllByTestId } = render() + const statusButtons = await findAllByTestId('status-item') + global.setFetchResponse({id: 1,name: 'theGrey', profile_image: './images/theGrey.webp', "status": "Offline"}) + fireEvent.click(statusButtons[0]) + expect(fetch).toHaveBeenCalledWith("http://localhost:6001/users/1", { + method: "patch", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({id: 1,name: 'theGrey', profile_image: './images/theGrey.jpg', "status": "Offline"}), + }) + const profiles = await findAllByTestId('user-item') + const changedProfile = profiles[0] + const status = changedProfile.querySelector("p").textContent + await waitFor(() => { + expect(status).toBe("Offline"); + }); + }); +}) \ No newline at end of file diff --git a/src/__tests__/test_suites/DisplayUsers.test.jsx b/src/__tests__/test_suites/DisplayUsers.test.jsx index e69de29..2f58cc7 100644 --- a/src/__tests__/test_suites/DisplayUsers.test.jsx +++ b/src/__tests__/test_suites/DisplayUsers.test.jsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import App from '../../components/App'; +import '@testing-library/jest-dom'; + +describe('Our app will', () => { + test('displays all users on startup', async () => { + global.setFetchResponse(global.baseUsers); + + const { findAllByTestId } = render(); + const userItems = await findAllByTestId('user-item'); + + // Check count + expect(userItems).toHaveLength(global.baseUsers.length); + + // Check names + const userNames = userItems.map((item) => + item.querySelector('h4').textContent + ); + const baseUsersNames = global.baseUsers.map((user) => user.name); + expect(userNames).toEqual(baseUsersNames); + + // ✅ Fix selector and split logic + const userProfileImage = userItems.map((item) => { + const imageEl = item.querySelector('.inline-block-child.profile-image'); + return imageEl ? imageEl.src.split('/').pop() : null; + }); + const baseUserProfileImages = global.baseUsers.map((user) => + user.profile_image.split('/').pop() + ); + expect(userProfileImage).toEqual(baseUserProfileImages); + + // Check statuses + const userStatus = userItems.map((item) => + item.querySelector('p').textContent + ); + const baseUserStatus = global.baseUsers.map((user) => user.status); + expect(userStatus).toEqual(baseUserStatus); + }); +}); diff --git a/src/components/App.jsx b/src/components/App.jsx index a49b230..829472e 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -5,9 +5,34 @@ import UserList from './UserList' function App() { const [users, setUsers] = useState([]) + useEffect(()=>{ + fetch('http://localhost:6001/users') + .then(r=>r.json()) + .then(data=>setUsers(data)) + },[]) + + function changeStatus(updatedUser){ + fetch(`http://localhost:6001/users/${updatedUser.id}`,{ + method: "patch", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(updatedUser), + } + ) + .then((r) => r.json()) + .then((data) => { + setUsers((prevUsers) => + prevUsers.map((user) => + user.id === updatedUser.id ? data : user + ) + ); + }); + } + return (
- +
) } diff --git a/src/components/UserList.jsx b/src/components/UserList.jsx index 2f2dc8b..89d0f5f 100644 --- a/src/components/UserList.jsx +++ b/src/components/UserList.jsx @@ -1,6 +1,17 @@ import React, { useState } from "react"; -function UserList({users}) { +function UserList({users,changeStatus}) { + function onClick(user){ + let updatedUser = {...user} + if (user.status == "Online"){ + updatedUser.status = "Offline" + } + else{ + updatedUser.status = "Online" + } + changeStatus(updatedUser) + console.log(updatedUser) + } return (