forked from Lateral-Link/haistack-coding-challenge
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from andrecego/feat/add-candidate-details-view
Add CandidateCard component and candidate details page
- Loading branch information
Showing
14 changed files
with
910 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
{ | ||
"welcomeToHaistack": "Welcome to Haistack", | ||
"candidates": "Candidates" | ||
"candidates": "Candidates", | ||
"candidate": { | ||
"candidateDetails": "Candidate Details" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,48 @@ | ||
.main { | ||
padding: 20px; | ||
/* http://meyerweb.com/eric/tools/css/reset/ | ||
v2.0 | 20110126 | ||
License: none (public domain) | ||
*/ | ||
|
||
html, body, div, span, applet, object, iframe, | ||
h1, h2, h3, h4, h5, h6, p, blockquote, pre, | ||
a, abbr, acronym, address, big, cite, code, | ||
del, dfn, em, img, ins, kbd, q, s, samp, | ||
small, strike, strong, sub, sup, tt, var, | ||
b, u, i, center, | ||
dl, dt, dd, ol, ul, li, | ||
fieldset, form, label, legend, | ||
table, caption, tbody, tfoot, thead, tr, th, td, | ||
article, aside, canvas, details, embed, | ||
figure, figcaption, footer, header, hgroup, | ||
menu, nav, output, ruby, section, summary, | ||
time, mark, audio, video { | ||
margin: 0; | ||
padding: 0; | ||
border: 0; | ||
font-size: 100%; | ||
font: inherit; | ||
vertical-align: baseline; | ||
} | ||
/* HTML5 display-role reset for older browsers */ | ||
article, aside, details, figcaption, figure, | ||
footer, header, hgroup, menu, nav, section { | ||
display: block; | ||
} | ||
body { | ||
line-height: 1; | ||
} | ||
ol, ul { | ||
list-style: none; | ||
} | ||
blockquote, q { | ||
quotes: none; | ||
} | ||
blockquote:before, blockquote:after, | ||
q:before, q:after { | ||
content: ''; | ||
content: none; | ||
} | ||
table { | ||
border-collapse: collapse; | ||
border-spacing: 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,19 @@ | ||
import React from 'react' | ||
import { createBrowserRouter } from 'react-router-dom' | ||
import { Home, Candidates } from '@/views/index.js' | ||
import { Home, Candidate } from '@/views/index.js' | ||
|
||
export const router = createBrowserRouter([ | ||
{ | ||
path: '/', | ||
element: <Home />, | ||
}, | ||
{ | ||
path: '/candidates', | ||
element: <Candidates />, | ||
path: 'candidates', | ||
children: [ | ||
{ | ||
path: ':id', | ||
element: <Candidate />, | ||
}, | ||
], | ||
}, | ||
]) |
38 changes: 38 additions & 0 deletions
38
app/javascript/src/tests/components/CandidateCard.spec.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { expect, describe, it, vi, afterEach } from 'vitest' | ||
import { render, cleanup } from '@testing-library/react' | ||
import CandidateCard from '@/components/CandidateCard.jsx' | ||
import createCandidate from '@/models/candidate' | ||
|
||
vi.mock('antd', () => ({ | ||
Card: ({ children }) => <div>{children}</div>, | ||
Avatar: ({ children }) => <div>{children}</div>, | ||
Flex: ({ children }) => <div>{children}</div>, | ||
Typography: { | ||
Title: ({ children }) => <div>{children}</div>, | ||
}, | ||
})) | ||
|
||
describe('CandidateCard', () => { | ||
afterEach(cleanup) | ||
|
||
const candidate = createCandidate({ | ||
id: 1, | ||
name: 'John Doe', | ||
email: 'john@doe.com', | ||
birthdate: '1990-01-01', | ||
}) | ||
|
||
it('renders a card with the candidate information', () => { | ||
const { getByText } = render(<CandidateCard candidate={candidate} loading={false} />) | ||
|
||
expect(getByText('John Doe')).toBeTruthy() | ||
expect(getByText('john@doe.com')).toBeTruthy() | ||
expect(getByText('1990/01/01')).toBeTruthy() | ||
}) | ||
|
||
it('renders a link to send an email to the candidate', () => { | ||
const { getByTestId } = render(<CandidateCard candidate={candidate} loading={false} />) | ||
|
||
expect(getByTestId('email-link').getAttribute('href')).toBe('mailto:john@doe.com') | ||
}) | ||
}) |
102 changes: 70 additions & 32 deletions
102
app/javascript/src/tests/repositories/candidates.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,59 +1,97 @@ | ||
import { describe, it, expect, vi } from 'vitest' | ||
import { get } from '@/lib/request.js' | ||
import { fetchCandidates } from '@/repositories/candidates.js' | ||
import { fetchCandidates, getCandidate } from '@/repositories/candidates.js' | ||
|
||
vi.mock('@/lib/request.js', () => ({ | ||
get: vi.fn(), | ||
})) | ||
|
||
vi.mock('@/models/candidate.js', () => ({ default: () => 'createCandidate' })) | ||
|
||
describe('fetchCandidates', () => { | ||
it('returns candidates and status', async () => { | ||
const candidates = [{ id: 1, name: 'John Doe' }] | ||
const response = { data: { candidates }, status: 200 } | ||
get.mockResolvedValue(response) | ||
|
||
const result = await fetchCandidates() | ||
|
||
expect(result.candidates).toEqual(['createCandidate']) | ||
expect(result.status).toBe(200) | ||
}) | ||
|
||
describe('when response has meta', () => { | ||
it('returns meta', async () => { | ||
describe('candidates repository', () => { | ||
describe('fetchCandidates', () => { | ||
it('returns candidates and status', async () => { | ||
const candidates = [{ id: 1, name: 'John Doe' }] | ||
const meta = { total: 1 } | ||
const response = { data: { candidates, meta }, status: 200 } | ||
const response = { data: { candidates }, status: 200 } | ||
get.mockResolvedValue(response) | ||
|
||
const result = await fetchCandidates() | ||
|
||
expect(result.meta).toEqual(meta) | ||
expect(result.candidates).toEqual(['createCandidate']) | ||
expect(result.status).toBe(200) | ||
}) | ||
}) | ||
|
||
describe('when response has no candidates', () => { | ||
it('returns empty array', async () => { | ||
const response = { data: {}, status: 200 } | ||
get.mockResolvedValue(response) | ||
describe('when response has meta', () => { | ||
it('returns meta', async () => { | ||
const candidates = [{ id: 1, name: 'John Doe' }] | ||
const meta = { total: 1 } | ||
const response = { data: { candidates, meta }, status: 200 } | ||
get.mockResolvedValue(response) | ||
|
||
const result = await fetchCandidates() | ||
const result = await fetchCandidates() | ||
|
||
expect(result.candidates).toEqual([]) | ||
expect(result.meta).toEqual(meta) | ||
}) | ||
}) | ||
|
||
describe('when response has no candidates', () => { | ||
it('returns empty array', async () => { | ||
const response = { data: {}, status: 200 } | ||
get.mockResolvedValue(response) | ||
|
||
const result = await fetchCandidates() | ||
|
||
expect(result.candidates).toEqual([]) | ||
}) | ||
}) | ||
|
||
describe('when response is not successful', () => { | ||
it('returns status', async () => { | ||
const response = { data: {}, status: 500 } | ||
get.mockResolvedValue(response) | ||
|
||
const result = await fetchCandidates() | ||
|
||
expect(result.candidates).toEqual([]) | ||
expect(result.status).toBe(500) | ||
}) | ||
}) | ||
}) | ||
|
||
describe('when response is not successful', () => { | ||
it('returns status', async () => { | ||
const response = { data: {}, status: 500 } | ||
describe('getCandidate', () => { | ||
it('returns candidate and status', async () => { | ||
const candidate = { id: 1, name: 'John Doe' } | ||
const response = { data: candidate, status: 200 } | ||
get.mockResolvedValue(response) | ||
|
||
const result = await fetchCandidates() | ||
const result = await getCandidate(1) | ||
|
||
expect(result.candidate).toEqual('createCandidate') | ||
expect(result.status).toBe(200) | ||
}) | ||
|
||
describe('when response is not successful', () => { | ||
it('returns status', async () => { | ||
const response = { data: {}, status: 500 } | ||
get.mockResolvedValue(response) | ||
|
||
expect(result.candidates).toEqual([]) | ||
expect(result.status).toBe(500) | ||
const result = await getCandidate(1) | ||
|
||
expect(result.candidate).toEqual('createCandidate') | ||
expect(result.status).toBe(500) | ||
}) | ||
}) | ||
|
||
describe('when response has no data', () => { | ||
it('returns status', async () => { | ||
const response = { status: 404 } | ||
get.mockResolvedValue(response) | ||
|
||
const result = await getCandidate(1) | ||
|
||
expect(result.candidate).toEqual('createCandidate') | ||
expect(result.status).toBe(404) | ||
}) | ||
}) | ||
}) | ||
}) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { expect, describe, it, vi, afterEach } from 'vitest' | ||
import { render, cleanup } from '@testing-library/react' | ||
import Candidate from '@/views/Candidate.jsx' | ||
import { getCandidate } from '@/repositories/candidates.js' | ||
import { useParams } from 'react-router-dom' | ||
|
||
vi.mock('antd', () => ({ | ||
Typography: { | ||
Title: ({ children }) => <div>{children}</div>, | ||
}, | ||
})) | ||
|
||
vi.mock('@/components/CandidateCard', () => ({ | ||
default: () => <div>CandidateCard</div>, | ||
})) | ||
|
||
vi.mock('react-router-dom', () => ({ | ||
useParams: vi.fn(() => ({ id: 1 })), | ||
})) | ||
|
||
vi.mock('react-i18next', () => ({ | ||
useTranslation: () => ({ t: (key) => key }), | ||
})) | ||
|
||
vi.mock('@/repositories/candidates.js', () => ({ | ||
getCandidate: vi.fn(() =>({ | ||
candidate: { | ||
id: 1, | ||
name: 'John Doe', | ||
email: 'john@doe.com', | ||
birthdate: '1990-01-01', | ||
}, | ||
})), | ||
})) | ||
|
||
describe('CandidateCard', () => { | ||
afterEach(cleanup) | ||
|
||
it('renders the title', () => { | ||
const { getByText } = render(<Candidate />) | ||
|
||
expect(getByText('candidate.candidateDetails')).toBeTruthy() | ||
}) | ||
|
||
it('calls the getCandidate function with the id', () => { | ||
useParams.mockReturnValue({ id: 2 }) | ||
|
||
render(<Candidate />) | ||
|
||
expect(getCandidate).toHaveBeenCalledWith(2) | ||
}) | ||
}) |
Oops, something went wrong.