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

Add participants list view and tests for hooks #7

Merged
merged 5 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions frontend/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const nextJest = require("next/jest");
const createJestConfig = nextJest({
dir: "./",
});
const customJestConfig = {
moduleDirectories: ["node_modules", "<rootDir>/"],
testEnvironment: "jest-environment-jsdom",
};
module.exports = createJestConfig(customJestConfig);
28,768 changes: 19,052 additions & 9,716 deletions frontend/package-lock.json

Large diffs are not rendered by default.

15 changes: 5 additions & 10 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
"@mui/material": "^5.9.2",
"@mui/styles": "^5.9.3",
"@mui/x-data-grid": "^6.0.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.0.1",
"@types/js-cookie": "^3.0.2",
Expand All @@ -22,6 +20,7 @@
"@typescript-eslint/parser": "^5.23.0",
"axios": "^0.27.2",
"check-prop-types": "^1.1.2",
"jest-environment-jsdom": "^29.7.0",
"next": "^13.4.19",
"react": "^18.2.0",
"react-data-table-component": "^7.5.2",
Expand All @@ -35,18 +34,11 @@
},
"scripts": {
"dev": "next dev",
"test": "jest --watch",
"proxy": "node proxy",
"build": "next build",
"start": "next start"
},
"jest": {
"collectCoverageFrom": [
"**/*.{js,jsx}",
"!**/node_modules/**",
"!**/coverage/**",
"!**/index.js"
]
},
"eslintConfig": {
"extends": [
"react-app",
Expand All @@ -66,6 +58,9 @@
]
},
"devDependencies": {
"@testing-library/jest-dom": "^6.1.3",
"@testing-library/react": "^14.0.0",
"jest": "^29.7.0",
"jest-canvas-mock": "^2.4.0",
"jest-enzyme": "^7.1.2",
"jest-watch-typeahead": "^0.6.5",
Expand Down
1 change: 1 addition & 0 deletions frontend/pages/participants/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Participants as default } from "../../src/pages/Participants"
Binary file added frontend/pages/users/.index.tsx.swp
Binary file not shown.
1 change: 1 addition & 0 deletions frontend/pages/users/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Users as default } from "../../src/pages/Users"
57 changes: 57 additions & 0 deletions frontend/src/components/ParticipantsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { DataGrid } from '@mui/x-data-grid';
import Box from '@mui/material/Box';
import {AppBar, Toolbar } from "@mui/material";
import useParticipants from "../hooks/useParticipants";
import type {NextPage} from 'next';
import SignUpForm from "../components/signUpForm";
import {Typography} from "@material-ui/core";

const ParticipantsList = () => {
const { participants, isLoading } = useParticipants();

const columns = [
{
field: 'id',
headerName: 'ID',
width: 90
},
{
field: 'first_name',
headerName: 'First Name',
width: 300,
editable: true,
},
{
field: 'last_name',
headerName: 'Last Name',
width: 300,
editable: true,
},
]

return (
<div>
<Box sx={{textAlign:'left', height: 400, width: '100%'}}>
<Typography variant={'h6'}> Participants </Typography>
<div style={{ height: 250, width: '100%' }}>
<DataGrid
rows={participants}
columns={columns}
initialState={{
pagination: {
paginationModel: {
pageSize: 5,
},
},
}}
pageSizeOptions={[5]}
checkboxSelection
disableRowSelectionOnClick
/>
</div>
</Box>
</div>
);
}

export default ParticipantsList;
69 changes: 69 additions & 0 deletions frontend/src/hooks/useParticipants.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {waitFor, render, screen, act} from "@testing-library/react";
import {AxiosInstance} from "axios";
import useParticipants from "./useParticipants";
import React from "react";
import {useHttpClient} from "./useHttpClient";

jest.mock("./useHttpClient")

const mockedUseHttpClient = jest.mocked(useHttpClient, true)
const TestParticipantsComponent = () => {
const {participants, isLoading} = useParticipants()
return (
<>{
isLoading ?
<></> :
<div>
{
participants.map((p) => {
return (
<>
<li>First Name: {p.first_name}</li>
<li>Last Name: {p.last_name}</li>
<li>ID: {p.id}</li>
</>
)
})
}
</div>
}
</>
)
}

describe('useParticipants', () => {
const mockHttpClient = {
get: jest.fn(),
post: jest.fn()
}
beforeEach(async () => {
const expectedParticipantsResponse = {
data: [
{
"id":1,
"first_name":"clayton",
"last_name":"johnson",
"birthdate":"2016-04-16",
}
]
}
mockedUseHttpClient.mockReturnValue(((mockHttpClient as unknown) as AxiosInstance))
mockHttpClient.get.mockReturnValue(Promise.resolve(expectedParticipantsResponse))
mockHttpClient.post.mockReturnValue(expectedParticipantsResponse)
});

it('should return renderable participant data', async () => {
act(() => {
render (
<TestParticipantsComponent/>
)
})

expect(mockHttpClient.get.mock.calls[0][0]).toEqual(`/participants`)
await waitFor(() => {
expect(mockHttpClient.get).toHaveBeenCalledWith('/participants')
expect(screen.getByText(/clayton/)).not.toBeNull()
expect(screen.getByText(/1/)).not.toBeNull()
})
});
});
32 changes: 15 additions & 17 deletions frontend/src/hooks/useParticipants.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import axios from "axios";
import {useHttpClient} from "./useHttpClient";
import {useEffect, useState} from "react";

interface Participant {
Expand All @@ -8,26 +8,24 @@ interface Participant {
birthdate?: string;
}

function useParticipants(): Participant[] {
function useParticipants(): { participants: Participant[], isLoading: boolean } {
const [apiData, setApiData] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const httpClient = useHttpClient();

useEffect(() => {
axios({
method: 'GET',
url: `/participants`,
headers: {
'Content-Type': 'application/json',
},
})
.then(response => {
console.log("This is the response: ", response)
setApiData(response.data)
})
.catch(error => {
console.log(error)
})
setIsLoading(true)
httpClient.get('/participants').then(response => {
setApiData(response.data)
setIsLoading(false)
return { participants: apiData, isLoading: isLoading }
})
.catch(error => {
console.log(error)
})
}, []);

return apiData ?? []
return { participants: apiData, isLoading: isLoading }
}

export default useParticipants;
9 changes: 6 additions & 3 deletions frontend/src/hooks/useUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,24 @@ interface User {
updated_at: string
}

function useUsers(): User[] {
function useUsers(): { users: User[], isLoading: boolean } {
const [apiData, setApiData] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const httpClient = useHttpClient();

useEffect(() => {
console.log(`httpClient BASE URL: ${httpClient.getUri()}`);
setIsLoading(true)
httpClient.get('/users').then(response => {
setApiData(response.data)
setIsLoading(false)
return { users: apiData, isLoading: isLoading }
})
.catch(error => {
console.log(error)
})
}, []);

return apiData ?? []
return { users: apiData, isLoading: isLoading }
}

export default useUsers;
28 changes: 28 additions & 0 deletions frontend/src/pages/Participants.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import ParticipantsList from "../components/ParticipantsList";
import {AppBar, Toolbar } from "@mui/material";
import SignUpForm from "../components/signUpForm";
import type {NextPage} from 'next';

export const Participants: NextPage = () => {
return (
<>
<AppBar position="static" sx={{ bgcolor: "pink" }}>
<section aria-label={"toolbar-section"}
style={{display: "flex", flexDirection: "row", justifyContent: "right"}}>
<Toolbar>
<SignUpForm/>
</Toolbar>
</section>
</AppBar>
<section aria-label={"participants-section"}
style={{
padding: "40px 30px",
display: "flex",
flexDirection: "column",
justifyContent: "space-around"
}}>
<ParticipantsList/>
</section>
</>
)
}
67 changes: 67 additions & 0 deletions frontend/src/pages/Users.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { DataGrid } from '@mui/x-data-grid';
import Box from '@mui/material/Box';
import {AppBar, Toolbar } from "@mui/material";
import useUsers from "../hooks/useUsers";
import type {NextPage} from 'next';
import SignUpForm from "../components/signUpForm";
import {Typography} from "@material-ui/core";

export const Users: NextPage = () => {
const { users, isLoading } = useUsers();

const columns = [
{ field: 'id', headerName: 'ID', width: 90 },
{
field: 'email',
headerName: 'Email',
width: 300,
editable: true,
},
{
field: 'updated_at',
headerName: 'Last Updated',
width: 300,
editable: true,
},
]

return (
<div>
<AppBar position="static" sx={{ bgcolor: "pink" }}>
<section aria-label={"toolbar-section"}
style={{display: "flex", flexDirection: "row", justifyContent: "right"}}>
<Toolbar>
<SignUpForm/>
</Toolbar>
</section>
</AppBar>
<section aria-label={"participants-section"}
style={{
padding: "40px 30px",
display: "flex",
flexDirection: "column",
justifyContent: "space-around"
}}>
<Box sx={{textAlign:'left', height: 400, width: '100%'}}>
<Typography variant={'h6'}> Users </Typography>
<div style={{ height: 250, width: '100%' }}>
<DataGrid
rows={users}
columns={columns}
initialState={{
pagination: {
paginationModel: {
pageSize: 5,
},
},
}}
pageSizeOptions={[5]}
checkboxSelection
disableRowSelectionOnClick
/>
</div>
</Box>
</section>
</div>
);
}
Loading
Loading