diff --git a/package-lock.json b/package-lock.json index 2e6ab8a..0bb7e39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "devDependencies": { "@eslint/js": "^9.11.1", "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.0.1", "@vitejs/plugin-react": "^4.3.1", "@vitest/coverage-v8": "^2.1.1", @@ -31,6 +32,13 @@ "vitest": "^2.1.1" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -2169,6 +2177,48 @@ "node": ">=18" } }, + "node_modules/@testing-library/jest-dom": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", + "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, "node_modules/@testing-library/react": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", @@ -3074,6 +3124,13 @@ "node": ">= 8" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, "node_modules/cssstyle": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", @@ -4417,6 +4474,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -5083,6 +5150,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -5218,6 +5292,16 @@ "node": ">= 0.6" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -5868,6 +5952,20 @@ "react-dom": ">=16.6.0" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -6448,6 +6546,19 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", diff --git a/package.json b/package.json index 8d8c12a..8b39b65 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "devDependencies": { "@eslint/js": "^9.11.1", "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.0.1", "@vitejs/plugin-react": "^4.3.1", "@vitest/coverage-v8": "^2.1.1", diff --git a/src/App.jsx b/src/App.jsx index 90bc5f1..df757b9 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,5 @@ import { useAuthState } from './utilities/firebase'; -import { HashRouter as Router, Routes, Route } from 'react-router-dom'; +import { HashRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; import 'bootstrap-icons/font/bootstrap-icons.css'; import 'bootstrap/dist/css/bootstrap.min.css'; @@ -45,7 +45,7 @@ const App = () => { - )} + )} ); diff --git a/src/App.test.jsx b/src/App.test.jsx index 6820dcc..92f7465 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -1,19 +1,50 @@ -import {describe, expect, test} from 'vitest'; +import { describe, it, expect, vi, test} from 'vitest'; import {fireEvent, render, screen} from '@testing-library/react'; +import { MemoryRouter, Routes, Route } from 'react-router-dom'; +import '@testing-library/jest-dom'; // Import jest-dom matchers + +import { BrowserRouter as Router } from 'react-router-dom'; import App from './App'; +import RequestFormPage from './components/pages/RequestForm'; +import HomePage from './components/pages/HomePage'; -describe('counter tests', () => { - - test("Counter should be 0 at the start", () => { - render(); - expect(screen.getByText('count is: 0')).toBeDefined(); - }); +describe('RequestFormPage', () => { + test('displays Meet Up textbox when Meet up option is selected', () => { + render( + + + + ); + const pickUpCheckbox = screen.getByTestId('checkbox-Meet up'); + fireEvent.click(pickUpCheckbox); + expect(pickUpCheckbox.checked).toBe(true); + screen.getByLabelText('Meet-up Location:'); - test("Counter should increment by one when clicked", async () => { - render(); - const counter = screen.getByRole('button'); - fireEvent.click(counter); - expect(await screen.getByText('count is: 1')).toBeDefined(); - }); + const input = screen.getByPlaceholderText(/Enter location/i) + expect(input).toBeInTheDocument(); + + }) + +}) -}); \ No newline at end of file +describe('send text from home page to request form', () => { + it('should send the text in the textbox to the textbox on the request form', () => { + render( + + + } /> + } /> + + + ); + const input = screen.getByPlaceholderText(/How can your neighbors help?/i); + fireEvent.change(input, { target: { value: 'I need a hammer!' } }); + + const submitButton = screen.getByText(/Submit/i); + fireEvent.click(submitButton); + + expect(screen.getByText(/New Request/i)).toBeInTheDocument(); + expect(screen.getByLabelText(/Description/i)).toHaveValue('I need a hammer!'); + }); + +}) \ No newline at end of file diff --git a/src/RequestForm.test.jsx b/src/RequestForm.test.jsx new file mode 100644 index 0000000..2f0ffe8 --- /dev/null +++ b/src/RequestForm.test.jsx @@ -0,0 +1,87 @@ +import { describe, it, vi, beforeEach, afterEach, expect } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/react'; +import { RequestForm } from './components/Form.jsx'; +import '@testing-library/jest-dom'; + +vi.mock('react-router-dom', async (importOriginal) => { + const actual = await importOriginal(); // Import the original module + return { + ...actual, + useNavigate: vi.fn(), // Mock only the useNavigate function + }; +}); + +import { MemoryRouter, useNavigate } from 'react-router-dom'; // Import after the mock + +describe('RequestForm Component', () => { + let mockSetDescription, mockSetTimer, mockSetDeliveryPref, mockSetMeetUpLocation, mockOnClick, mockNavigate; + + beforeEach(() => { + mockSetDescription = vi.fn(); + mockSetTimer = vi.fn(); + mockSetDeliveryPref = vi.fn(); + mockSetMeetUpLocation = vi.fn(); + mockOnClick = vi.fn(); + mockNavigate = vi.fn(); + useNavigate.mockReturnValue(mockNavigate); // Correctly mock the useNavigate hook + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + const renderComponent = (deliveryPref = [], meetUpLocation = '') => { + render( + + + + ); + }; + + it('renders description input', () => { + renderComponent(); + expect(screen.getByPlaceholderText(/how can your neighbors help/i)).toBeInTheDocument(); + }); + + it('renders post expiration section', () => { + renderComponent(); + expect(screen.getByText(/post expiration/i)).toBeInTheDocument(); + }); + + it('renders delivery preference section', () => { + renderComponent(); + expect(screen.getByText(/delivery preference/i)).toBeInTheDocument(); + }); + + it('shows Meet Up textbox when "Meet Up" option is selected', () => { + renderComponent(['Meet up']); // Pass 'Meet up' as a selected delivery option + expect(screen.getByLabelText(/Meet-up Location/i)).toBeInTheDocument(); + }); + + it('does not show Meet Up textbox when "Meet Up" option is not selected', () => { + renderComponent(['Pick up']); // Pass a different option + expect(screen.queryByLabelText(/meet up location/i)).not.toBeInTheDocument(); + }); + + it('triggers the Cancel button navigation', () => { + renderComponent(); + const cancelButton = screen.getByText(/cancel/i); + fireEvent.click(cancelButton); + expect(mockNavigate).toHaveBeenCalledWith('/'); + }); + + it('triggers the Submit button handler', () => { + renderComponent(); + const submitButton = screen.getByText(/submit/i); + fireEvent.click(submitButton); + expect(mockOnClick).toHaveBeenCalled(); + }); +}); diff --git a/src/components/Form.jsx b/src/components/Form.jsx index d304d83..63e6f29 100644 --- a/src/components/Form.jsx +++ b/src/components/Form.jsx @@ -221,12 +221,14 @@ const MultiSelect = ({deliveryPref, setDeliveryPref, meetUp, setMeetUpLocation}) label={option} checked={deliveryPref.includes(option)} onChange={() => handleSelect(option)} // Handle checkbox change + data-testid={`checkbox-${option}`} /> ))} {deliveryPref.includes('Meet up') && ( - Meet-up Location: + Meet-up Location: