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

WPP15 - User Page #63

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions client/PageRoutes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import LandingPage from './pages/LandingPage/LandingPage.jsx'
import Puzzle1Page from './pages/Puzzle1Page/Puzzle1Page';
import { Error404 } from './components/Errors/Errors.jsx';
import PuzzleSelectionPage from './pages/PuzzleSelectionPage/PuzzleSelectionPage.jsx';
import UserPage from './pages/UserPage/UserPage.jsx';
import Verify from './components/Verify/Verify.jsx';

export default function PageRoutes() {
Expand All @@ -25,6 +26,7 @@ export default function PageRoutes() {
<Route index element={<LandingPage />} />
<Route path="/Puzzle/Selection" element={<Verify><PuzzleSelectionPage /></Verify>} />
<Route path="/Puzzle/1" element={<Verify><Puzzle1Page /></Verify>} />
<Route path="/UserProfile/:userid" element={<Verify><UserPage /></Verify>}/>
<Route path="*" element={<Error404 />} />
</Routes>
</>
Expand Down
16 changes: 16 additions & 0 deletions client/api/DataHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ export function logIn(userData) {
});
}

export function changePassword(userData) {
const { newPassword } = userData;
return new Promise((resolve, reject) => {
axios
.post("api/userInfo", {
newPassword,
})
.then(function (res) {
return resolve(res);
})
.catch(function (err) {
return reject(err);
});
});
}

//Function that calls the listPuzzles get
export function listPuzzles() {
return new Promise((resolve, reject) => {
Expand Down
7 changes: 7 additions & 0 deletions client/components/ChangePassword/ChangePassword.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.changePassword_Label{
color:black;
}

.changePassword_Submit{
background-color: lightgreen;
}
93 changes: 93 additions & 0 deletions client/components/ChangePassword/ChangePassword.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { Component } from "react";
import { useNavigate } from "react-router-dom";
import { Button, Form, Row } from "react-bootstrap";
import { changePassword } from "../../api/DataHelper";
import "./ChangePassword.css"
import { checkPasswordRequirements } from "../../../utilities/AccountValidators";

//Default values for form data
const initialFormData = Object.freeze({
currentPassword: "",
newPassword: "",
confirmNewPassword: "",
});


function ChangePassword(props) {
//formData is an object that holds currentPassword, newPassword, and confirmNewPassword
const [formData, updateFormData] = React.useState(initialFormData);
const navigate = useNavigate();
//Whenever username or confirmPassword input boxes change, this saves the new data to formData
const handleChange = (e) => {
updateFormData({
...formData,
[e.target.name]: e.target.value.trim(),
});
};
//See if new password meets requirements, and new password is confirmed, then close page
const handleSubmit = (e) => {
e.preventDefault();
try {
if(checkPasswordRequirements(formData.newPassword) &&
(formData.newPassword == formData.confirmNewPassword)) {
changePassword(formData)
.then((res) => {
//No need to navigate anywhere else from this point.
//Give user the option to continue viewing user profile
alert("Password successfully changed")
})
.catch((err) => {
alert(err)
})
}
console.log("Hey this code works");
props.close();
} catch (error) {
alert(error.message);
}
};
return(
<Form onSubmit={handleSubmit}>
<Row className="changePassword_Label">
<Form.Text>Enter Current Password:</Form.Text>
<Form.Control
type="password"
data-testid="currentPassword"
name="currentPassword"
onChange={handleChange}
/>
</Row>
<Row className="changePassword_Label">
<Form.Text>Enter New Password:</Form.Text>
<Form.Control
type="password"
data-testid="newPassword"
name="newPassword"
onChange={handleChange}
/>
</Row>
<Row className="changePassword_Label">
<Form.Text>Confirm New Password:</Form.Text>
<Form.Control
type="password"
data-testid="confirmNewPassword"
name="confirmNewPassword"
onChange={handleChange}
/>
</Row>
<Row>
<Button
className="changePassword_Submit"
variant="primary"
type="submit"
data-testid="changePassword_Submit"
onClick={handleSubmit}
>
Change Password
</Button>
</Row>
</Form>
);
}

export { ChangePassword };
22 changes: 22 additions & 0 deletions client/pages/UserPage/UserPage.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.userPage_ProfilePicture {
display: block;
margin: auto;
}

.userPage_Title{
color: white;
text-align: center;
font-size: large;
}

.userPage_Username{
color: white;
text-align: center;
}

.userPage_Button{
background-color: lightgreen;
text-align: center;
margin: auto;
display: block;
}
69 changes: 69 additions & 0 deletions client/pages/UserPage/UserPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { useState } from 'react';
import { Link, useNavigate } from "react-router-dom";
import { Button, Form, Container, Modal } from "react-bootstrap";
import { ChangePassword } from "../../components/ChangePassword/ChangePassword";
import "./UserPage.css";

export default function UserPage () {
const userIcon =
"https://api.dicebear.com/5.x/adventurer/svg?seed=Gracie&scale=130&radius=20&backgroundType=solid,gradientLinear&randomizeIds=true&backgroundColor=c0aede,b6e3f4,d1d4f9,ffdfbf,ffd5dc";

const [showChange, setShowChange] = useState(false);

const handleShowChange = () => setShowChange(true);
const handleCloseChange = () => setShowChange(false);

//Default values for form data
const initialFormData = Object.freeze({
currentPassword: "",
newPassword: "",
confirmNewPassword: "",
});

const [formData, updateFormData] = React.useState(initialFormData);

const handleChange = (e) => {
updateFormData({
...formData,
[e.target.name]: e.target.value.trim()
});
}

const navigate = useNavigate();

return(
<div className="UserPage" data-testid="userPage">
<div>
<p className="userPage_Title">User Profile</p>
</div>
<div>
<img className="userPage_ProfilePicture" src={userIcon} data-testid="profilePic" height="75" />
</div>
<div>
<p className="userPage_Username" data-testid="usename"> Username goes here! </p>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't it be better to actually display the username, as is mentioned in this user story

</div>
<div>
<Button
className="userPage_Button"
variant="secondary"
data-testid="changePassword"
onClick={handleShowChange}>
Change Password
</Button>
<Modal show={showChange} onHide={handleCloseChange}>
<Modal.Header closeButton>
<Modal.Title>Change Password</Modal.Title>
</Modal.Header>
<Modal.Body>
<ChangePassword />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleCloseChange}>
Close
</Button>
</Modal.Footer>
</Modal>
</div>
</div>
)
}
2 changes: 1 addition & 1 deletion server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ app.use(Express.static('public'))

app.use('/api', dataRouter)

app.get(['/Puzzle/Selection', '/Puzzle/1', "*" ], (req, res) => {
app.get(['/Puzzle/Selection', '/Puzzle/1', '/UserProfile/:userid', "*" ], (req, res) => {
res.sendFile(path.join(__dirname, '../public', 'index.html'))
})

Expand Down
62 changes: 62 additions & 0 deletions tests/client/ChangePassword.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ChangePassword } from "../../client/components/ChangePassword/ChangePassword";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { BrowserRouter } from "react-router-dom";

describe("Tests for Change Password", () => {
beforeAll(() => {
jest.spyOn(console, "log").mockImplementation(() => {});
jest.spyOn(console, "error").mockImplementation(() => {});
});

test("Checks for current password label", () => {
const wrapper = render(
<BrowserRouter>
<ChangePassword/>
</BrowserRouter>
);
expect(wrapper.baseElement.outerHTML).toContain("Enter Current Password:");
});

test("Checks for new password label", () => {
const wrapper = render(
<BrowserRouter>
<ChangePassword/>
</BrowserRouter>
);
expect(wrapper.baseElement.outerHTML).toContain("Enter New Password:");
});

test("Checks for confirm new password label", () => {
const wrapper = render(
<BrowserRouter>
<ChangePassword/>
</BrowserRouter>
);
expect(wrapper.baseElement.outerHTML).toContain("Confirm New Password:");
});

test("Checks for successful form submission", () => {
const consoleSpy = jest.spyOn(global.console, "log");
const close = jest.fn();
const wrapper = render(
<BrowserRouter>
<ChangePassword close={close}/>
</BrowserRouter>
);

const inputCurrentPassword = screen.getByTestId("currentPassword");
userEvent.type(inputCurrentPassword, "Connor123");

const inputNewPassword = screen.getByTestId("newPassword");
userEvent.type(inputNewPassword, "Connor1234");

const inputConfirmNewPassword = screen.getByTestId("confirmNewPassword");
userEvent.type(inputConfirmNewPassword, "Connor1234");

const submitButton = screen.getByTestId("changePassword_Submit");
userEvent.click(submitButton);

expect(consoleSpy).toBeCalledWith("Hey this code works");
});
});
45 changes: 45 additions & 0 deletions tests/client/UserPage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import UserPage from "../../client/pages/UserPage/UserPage";
import { render, screen } from "@testing-library/react";
import { BrowserRouter } from "react-router-dom";

describe("Tests for User Page", () => {
beforeAll(() => {
jest.spyOn(console, "log").mockImplementation();
jest.spyOn(console, "error").mockImplementation();
});

test("Checks for User Profile header", () => {
const { baseElement } = render(
<BrowserRouter>
<UserPage/>
</BrowserRouter>
);
expect(baseElement.outerHTML).toContain("User Profile");
});

test("Checks for user profile picture", () => {
render(
<UserPage url="/UserProfile"/>, {wrapper: BrowserRouter}
);

expect(screen.getByTestId("profilePic").outerHTML).toContain("https://api.dicebear.com/5.x/adventurer/svg?seed=Gracie&amp;scale=130&amp;radius=20&amp;backgroundType=solid,gradientLinear&amp;randomizeIds=true&amp;backgroundColor=c0aede,b6e3f4,d1d4f9,ffdfbf,ffd5dc");
});

test("Checks for username header", () => {
const { baseElement } = render(
<BrowserRouter>
<UserPage/>
</BrowserRouter>
);
expect(baseElement.outerHTML).toContain("Username goes here!");
});

test("Checks for change password button", () => {
const { baseElement } = render(
<BrowserRouter>
<UserPage/>
</BrowserRouter>
);
expect(baseElement.outerHTML).toContain("Change Password");
});
});