Skip to content

Commit 0fb374c

Browse files
committed
DX-381 Build candidate listing page
1 parent 3448af7 commit 0fb374c

File tree

16 files changed

+2050
-443
lines changed

16 files changed

+2050
-443
lines changed

.nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v16

client/package.json

+9
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,21 @@
66
"@testing-library/jest-dom": "^5.16.4",
77
"@testing-library/react": "^13.3.0",
88
"@testing-library/user-event": "^13.5.0",
9+
"bootstrap": "^5.1.3",
910
"react": "^18.1.0",
11+
"react-bootstrap": "^2.4.0",
1012
"react-dom": "^18.1.0",
1113
"react-router-dom": "^6.3.0",
1214
"react-scripts": "5.0.1",
1315
"web-vitals": "^2.1.4"
1416
},
17+
"devDependencies": {
18+
"@testing-library/jest-dom": "^5.16.4",
19+
"@testing-library/react": "^13.3.0",
20+
"jest": "^28.1.1",
21+
"jest-environment-jsdom": "^28.1.1",
22+
"msw": "^0.42.1"
23+
},
1524
"proxy": "http://localhost:8000/",
1625
"scripts": {
1726
"start": "react-scripts start",

client/src/App.test.js

-8
This file was deleted.

client/src/Candidates.jsx

-161
This file was deleted.
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import React from 'react'
2+
import {rest} from 'msw'
3+
import {render, screen, waitFor} from '@testing-library/react'
4+
import {CandidatesPageObject} from '../../test/page_objects/Candidates'
5+
import CandidatesPage from '../components/candidates/CandidatesPage'
6+
import {candidates as candidates_fixture} from '../../test/fixtures/candidates.json'
7+
import {httpSetup} from '../../test/helpers/httpSetup'
8+
9+
describe('Candidates page', () => {
10+
let editCandidateRequestPayload = {}
11+
let createCandidateRequestPayload = {}
12+
const page = new CandidatesPageObject(screen)
13+
const serverMock = new httpSetup()
14+
15+
beforeAll(() => serverMock.listen())
16+
afterAll(() => serverMock.close())
17+
18+
it('displays an error when no candidates are found', async () => {
19+
serverMock.httpMockGet('candidates', [])
20+
render(<CandidatesPage />)
21+
22+
await page.initialLoad()
23+
await page.pageErrors('No candidates found')
24+
})
25+
26+
it('displays a list of candidates when available', async () => {
27+
serverMock.httpMockGet('candidates', candidates_fixture)
28+
render(<CandidatesPage />)
29+
30+
await page.initialLoad()
31+
page.validateElementLength('candidate-item', candidates_fixture.length)
32+
})
33+
34+
it('updates an existing candidate', async () => {
35+
const testCandidate = candidates_fixture[0]
36+
const expectedEditCandidateRequestPayload = {
37+
38+
name: 'Bart Simpson',
39+
notes: 'Hello World',
40+
phone: '393281929',
41+
step: 'Rejected',
42+
}
43+
44+
serverMock.server.use(
45+
rest.put(`/api/candidates/${testCandidate.id}`, async (req, res, ctx) => {
46+
editCandidateRequestPayload = req.body
47+
return res(ctx.json({id: 'f'}))
48+
}),
49+
)
50+
51+
render(<CandidatesPage />)
52+
53+
await page.initialLoad()
54+
55+
page.clickCandidate(testCandidate.name)
56+
page.clickEdit(testCandidate.id)
57+
page.inputEmail('[email protected]')
58+
page.inputName('Bart Simpson')
59+
page.inputNotes('Hello World')
60+
page.inputPhoneNumber('393281929')
61+
page.inputStep('Rejected')
62+
page.clickSaveChanges()
63+
64+
await waitFor(() => {
65+
expect(editCandidateRequestPayload).toMatchObject(
66+
expectedEditCandidateRequestPayload,
67+
)
68+
})
69+
})
70+
71+
it('creates a new candidate', async () => {
72+
const expectedCreateCandidateRequestPayload = {
73+
74+
name: 'Bart Simpson',
75+
notes: 'Hello World',
76+
phone: '393281929',
77+
step: 'Rejected',
78+
}
79+
80+
serverMock.server.use(
81+
rest.post('/api/candidates/', async (req, res, ctx) => {
82+
createCandidateRequestPayload = req.body
83+
return res(ctx.json({id: 'f'}))
84+
}),
85+
)
86+
87+
render(<CandidatesPage />)
88+
89+
await page.initialLoad()
90+
91+
page.clickAdd()
92+
page.inputEmail('[email protected]')
93+
page.inputName('Bart Simpson')
94+
page.inputNotes('Hello World')
95+
page.inputPhoneNumber('393281929')
96+
page.inputStep('Rejected')
97+
page.clickSubmit()
98+
99+
await waitFor(() => {
100+
expect(createCandidateRequestPayload).toMatchObject(
101+
expectedCreateCandidateRequestPayload,
102+
)
103+
})
104+
})
105+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react'
2+
import {Accordion, Button, Tabs, Tab} from 'react-bootstrap'
3+
4+
export default function Candidate({
5+
id,
6+
email,
7+
name,
8+
phone,
9+
notes,
10+
step,
11+
createdAt,
12+
handleEdit,
13+
}) {
14+
return (
15+
<Accordion.Item eventKey={id} data-testid="candidate-item">
16+
<Accordion.Header>
17+
<span className="w-25">{name}</span>
18+
<span className="w-25">
19+
<span className="badge bg-secondary">{step}</span>
20+
</span>
21+
<span className="text-muted small w-25 text-end">
22+
<span className="me-3">
23+
{new Date(createdAt).toLocaleDateString('en-US')}
24+
</span>
25+
</span>
26+
</Accordion.Header>
27+
<Accordion.Body>
28+
<Tabs defaultActiveKey="profile">
29+
<Tab eventKey="profile" title="Profile">
30+
<div className="p-3">
31+
<dl>
32+
<dt>Email</dt>
33+
<dd>{email}</dd>
34+
<dt>Phone</dt>
35+
<dd>{phone}</dd>
36+
<dt>Notes</dt>
37+
<dd>{notes}</dd>
38+
</dl>
39+
<Button
40+
onClick={() => handleEdit(true)}
41+
data-testid={`edit-${id}`}
42+
>
43+
Edit
44+
</Button>
45+
</div>
46+
</Tab>
47+
<Tab eventKey="background-checks" title="Background Checks">
48+
<div
49+
className="tab-pane p-3"
50+
id={`candidate-tab-content-${id}-background-checks`}
51+
>
52+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Similique
53+
animi maxime exercitationem perferendis quae? Laudantium dolorem
54+
debitis earum quidem assumenda, quibusdam perspiciatis mollitia
55+
facere aut impedit rem quas animi cum.
56+
</div>
57+
</Tab>
58+
</Tabs>
59+
</Accordion.Body>
60+
</Accordion.Item>
61+
)
62+
}

0 commit comments

Comments
 (0)