Skip to content

Commit

Permalink
Merge pull request #130 from DDD-Community/refactor/home-page
Browse files Browse the repository at this point in the history
[REFACTOR] 홈, 보드 생성 페이지 리팩토링 및 테스트 코드 추가
  • Loading branch information
junseublim authored Oct 8, 2024
2 parents 278f198 + 893327f commit b966742
Show file tree
Hide file tree
Showing 15 changed files with 436 additions and 56 deletions.
47 changes: 47 additions & 0 deletions src/__tests__/hooks/useBoardName.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { act, renderHook } from '@testing-library/react'
import { useBoardName } from '@/hooks/useBoardName'

describe('useBoardName()', () => {
it('description', () => {
// Given
const { result } = renderHook(() => useBoardName())

// When
act(() => {
result.current.setBoardName('board name')
})

// Then
expect(result.current.description).toEqual('10/15자')
})

describe('errorMessage', () => {
it('should return error message on empty string', () => {
// Given
const { result } = renderHook(() => useBoardName())

// When
act(() => {
result.current.setBoardName('')
})

// Then
expect(result.current.errorMessage).toEqual(
'최소 한글자 이상 입력해주세요',
)
})

it('should return error message on value longer than max length', () => {
// Given
const { result } = renderHook(() => useBoardName())

// When
act(() => {
result.current.setBoardName('abcdefghijklmnop')
})

// Then
expect(result.current.errorMessage).toEqual('15자 이내로 입력 가능해요')
})
})
})
182 changes: 182 additions & 0 deletions src/__tests__/hooks/useInputValidation.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { act, renderHook } from '@testing-library/react'
import { useInputValidation, Validation } from '@/hooks/useInputValidation'

describe('useInputValidation()', () => {
describe('value', () => {
it('should return initialValue as value', () => {
// Given
const initialValue = 'initialValue'

// When
const { result } = renderHook(() => useInputValidation(initialValue))

// Then
expect(result.current.value).toEqual(initialValue)
})

it('should set value to the new value when setValue is called', () => {
// Given
const initialValue = 'initialValue'
const { result } = renderHook(() => useInputValidation(initialValue))

// When
const newValue = 'newValue'
act(() => {
result.current.setValue(newValue)
})

// Then
expect(result.current.value).toEqual(newValue)
})
})
describe('isDirty', () => {
it('should set isDirty to false before the value is changed', () => {
// Given
const initialValue = 'initialValue'

// When
const { result } = renderHook(() => useInputValidation(initialValue))

// Then
expect(result.current.isDirty).toBeFalsy()
})

it('should set isDirty to true when the value is changed', () => {
// Given
const initialValue = 'initialValue'
const { result } = renderHook(() => useInputValidation(initialValue))

// When
const newValue = 'newValue'
act(() => {
result.current.setValue(newValue)
})

// Then
expect(result.current.isDirty).toBeTruthy()
})

it('should set isDirty to false when the value is not changed', () => {
// Given
const initialValue = 'initialValue'
const { result } = renderHook(() => useInputValidation(initialValue))

// When
const newValue = 'initialValue'
act(() => {
result.current.setValue(newValue)
})

// Then
expect(result.current.isDirty).toBeFalsy()
})
})

describe('isInvalid', () => {
it('should set isInvalid to true when a validation fails', () => {
// Given
const initialValue = 'initialValue'
const validations: Validation<string>[] = [
{
validator: () => false,
errorMessage: 'error message1',
},
{
validator: () => true,
errorMessage: 'error message2',
},
]

// When
const { result } = renderHook(() =>
useInputValidation(initialValue, validations),
)

// Then
expect(result.current.isInvalid).toBeTruthy()
})

it('should set isInvalid to false when all validations pass', () => {
// Given
const initialValue = 'initialValue'
const validations: Validation<string>[] = [
{
validator: () => true,
errorMessage: 'error message1',
},
{
validator: () => true,
errorMessage: 'error message2',
},
{
validator: () => true,
errorMessage: 'error message3',
},
]

// When
const { result } = renderHook(() =>
useInputValidation(initialValue, validations),
)

// Then
expect(result.current.isInvalid).toBeFalsy()
})
})

describe('errorMessage', () => {
it('should set errorMessage to the first validation that fails ', () => {
// Given
const initialValue = 'initialValue'
const validations: Validation<string>[] = [
{
validator: () => false,
errorMessage: 'error message1',
},
{
validator: () => false,
errorMessage: 'error message2',
},
{
validator: () => false,
errorMessage: 'error message3',
},
]

// When
const { result } = renderHook(() =>
useInputValidation(initialValue, validations),
)

// Then
expect(result.current.errorMessage).toEqual('error message1')
})

it('should set errorMessage to empty string when all validations pass', () => {
// Given
const initialValue = 'initialValue'
const validations: Validation<string>[] = [
{
validator: () => true,
errorMessage: 'error message1',
},
{
validator: () => true,
errorMessage: 'error message2',
},
{
validator: () => true,
errorMessage: 'error message3',
},
]

// When
const { result } = renderHook(() =>
useInputValidation(initialValue, validations),
)

// Then
expect(result.current.errorMessage).toEqual('')
})
})
})
39 changes: 39 additions & 0 deletions src/__tests__/lib/utils/clipboard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { copyToClipboard } from '@/lib/utils'

describe('lib/utils/clipboard', () => {
describe('copyToClipboard()', () => {
beforeEach(() => {
Object.assign(navigator, {
clipboard: {
writeText: jest.fn().mockImplementation(() => Promise.resolve()),
},
})
})

afterEach(() => {
jest.restoreAllMocks()
})

it('Should Copy target string to clipboard', async () => {
// Given
const target = 'copy target string'

// When
await copyToClipboard(target)

// Then
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(target)
})

it('Should return resolved promise object when it is done', () => {
// Given
const target = 'copy target string'

// When
const result = copyToClipboard(target)

// Then
expect(result).toEqual(Promise.resolve())
})
})
})
28 changes: 12 additions & 16 deletions src/app/(home)/_components/CopyLinkBtn.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
'use client'

import LinkIcon from 'public/icons/linkcopy.svg'
import TwoPolaroidsIcon from 'public/icons/twopolaroids.svg'
import Modal from '@/components/Modal'
import { useState } from 'react'
import LinkCopiedModal from '@/app/(home)/_components/LinkCopiedModal'
import { copyToClipboard } from '@/lib/utils'

const CopyLinkBtn = () => {
const [showLinkCopyModal, setShowLinkCopyModal] = useState(false)
const [isLinkCopiedModalOpen, setIsLinkCopiedModalOpen] = useState(false)

const closeModal = () => setShowLinkCopyModal(false)
const openLinkCopiedModal = () => setIsLinkCopiedModalOpen(true)
const closeLinkCopiedModal = () => setIsLinkCopiedModalOpen(false)

const copyLink = () => {
const copyCurrentUrl = () => {
const currentURL = window.location.href
return navigator.clipboard.writeText(currentURL).then(() => {
setShowLinkCopyModal(true)
})
return copyToClipboard(currentURL).then(openLinkCopiedModal)
}

return (
Expand All @@ -26,17 +25,14 @@ const CopyLinkBtn = () => {
type="button"
className="rounded-[30px] bg-gray-100 p-3 shadow-[0_4px_8px_0_rgba(0,0,0,0.15)]"
aria-label="copy link"
onClick={copyLink}
onClick={copyCurrentUrl}
>
<LinkIcon />
</button>
<Modal isOpen={showLinkCopyModal} onClose={closeModal}>
<Modal.CenterModal icon={<TwoPolaroidsIcon />}>
<Modal.Title>링크가 복사되었습니다!</Modal.Title>
<Modal.Content>{'POLABO를\n 지인들에게도 알려주세요!'}</Modal.Content>
<Modal.CenterConfirm confirmText="확인" />
</Modal.CenterModal>
</Modal>
<LinkCopiedModal
isOpen={isLinkCopiedModalOpen}
onClose={closeLinkCopiedModal}
/>
</>
)
}
Expand Down
12 changes: 6 additions & 6 deletions src/app/(home)/_components/CreateBoardBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import GoToLoginModal from './GoToLoginModal'

const CreateBoardBtn = () => {
const router = useRouter()
const [loginModalOpen, setLoginModalOpen] = useState(false)
const [isLoginModalOpen, setIsLoginModalOpen] = useState(false)

const { status } = useSession()

const handleClick = () => {
const handleCreateButtonClick = () => {
if (status === 'authenticated') {
router.push('/board/create')
} else {
setLoginModalOpen(true)
setIsLoginModalOpen(true)
}
}

Expand All @@ -25,13 +25,13 @@ const CreateBoardBtn = () => {
<Button
size="lg"
className="mb-3 shadow-[0px_0px_20px_0px_rgba(255,255,255,0.6)]"
onClick={handleClick}
onClick={handleCreateButtonClick}
>
보드 만들기
</Button>
<GoToLoginModal
isOpen={loginModalOpen}
onClose={() => setLoginModalOpen(false)}
isOpen={isLoginModalOpen}
onClose={() => setIsLoginModalOpen(false)}
/>
</>
)
Expand Down
25 changes: 25 additions & 0 deletions src/app/(home)/_components/LinkCopiedModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react'
import Modal from '@/components/Modal'
import TwoPolaroidsIcon from 'public/icons/twopolaroids.svg'

interface LinkCopiedModalProps {
isOpen: boolean
onClose: () => void
}

const LinkCopiedModal = ({
isOpen,
onClose: handleCloseModal,
}: LinkCopiedModalProps) => {
return (
<Modal isOpen={isOpen} onClose={handleCloseModal}>
<Modal.CenterModal icon={<TwoPolaroidsIcon />}>
<Modal.Title>링크가 복사되었습니다!</Modal.Title>
<Modal.Content>{'POLABO를\n 지인들에게도 알려주세요!'}</Modal.Content>
<Modal.CenterConfirm confirmText="확인" />
</Modal.CenterModal>
</Modal>
)
}

export default LinkCopiedModal
Loading

0 comments on commit b966742

Please sign in to comment.