diff --git a/packages/tooltip/src/Tooltip.test.tsx b/packages/tooltip/src/Tooltip.test.tsx index edf9269..9ae47a7 100644 --- a/packages/tooltip/src/Tooltip.test.tsx +++ b/packages/tooltip/src/Tooltip.test.tsx @@ -1,9 +1,21 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { useEffect, useState } from 'react'; import { describe, expect, test } from 'vitest'; import { Tooltip } from './Tooltip'; describe('Tooltip 기본 동작 테스트', () => { + test('Tooltip은 초기 상태에서 보이지 않아야 한다.', () => { + render( + + + , + ); + + const tooltip = screen.queryByText('Test Tooltip'); + expect(tooltip).not.toBeInTheDocument(); + }); + test('tooltipContent를 주입하지 않으면 Tooltip이 렌더링되지 않는다.', () => { render( @@ -47,12 +59,37 @@ describe('Tooltip 기본 동작 테스트', () => { await userEvent.click(trigger); expect(screen.queryByText('This is a tooltip')).not.toBeInTheDocument(); }); + + test('Tooltip 외부 클릭 시 닫힌다.', async () => { + render( +
+ + + + +
, + ); + + const trigger = screen.getByText('Click me'); + const outside = screen.getByText('Outside'); + + await userEvent.click(trigger); + expect(screen.getByText('This is a tooltip')).toBeInTheDocument(); + + await userEvent.click(outside); + expect(screen.queryByText('This is a tooltip')).not.toBeInTheDocument(); + }); }); describe('Tooltip 위치 테스트', () => { - test('Tooltip이 기본적으로 top 위치에 렌더링된다. (기본값 검증)', async () => { + test.each([ + ['top', 'top'], + ['bottom', 'bottom'], + ['left', 'left'], + ['right', 'right'], + ])('Tooltip이 %s 위치에 렌더링된다.', async (placement, expectedClass) => { render( - + , ); @@ -61,7 +98,7 @@ describe('Tooltip 위치 테스트', () => { await userEvent.hover(trigger); const tooltip = screen.getByText('This is a tooltip'); - expect(tooltip.className).toContain('top'); + expect(tooltip.className).toContain(expectedClass); }); }); @@ -97,10 +134,13 @@ describe('Tooltip 접근성 테스트', () => { }); }); -describe('Tooltip Portal 테스트', () => { - test('Portal을 사용해 Tooltip이 DOM의 최상위 레벨에 렌더링된다.', async () => { +describe('Tooltip 스타일 테스트', () => { + test('props로 주입한 backgroundColor와 padding이 CSS 변수에 반영된다.', async () => { render( - + , ); @@ -108,11 +148,12 @@ describe('Tooltip Portal 테스트', () => { const trigger = screen.getByText('Hover me'); await userEvent.hover(trigger); - const tooltip = screen.getByText('This is a tooltip'); - expect(tooltip.parentElement).toBe(document.body); + const tooltip = screen.getByText('Styled Tooltip'); + expect(tooltip).toHaveStyle('background-color: red'); + expect(tooltip).toHaveStyle('padding: 20px'); }); - test('Tooltip이 뷰포트 밖으로 벗어나지 않도록 위치를 조정한다.', async () => { + test('Tooltip이 뷰포트 밖으로 벗어나지 않는다.', async () => { render( , @@ -146,8 +185,41 @@ describe('Tooltip 스타일 테스트', () => { const trigger = screen.getByText('Hover me'); await userEvent.hover(trigger); - const tooltip = screen.getByText('Styled Tooltip'); - expect(tooltip).toHaveStyle('background-color: red'); - expect(tooltip).toHaveStyle('padding: 20px'); + const tooltip = screen.getByText(/This is a very long tooltip text/); + expect(tooltip).toHaveStyle('max-width: 150px'); + expect(tooltip).toHaveStyle('white-space: normal'); + }); +}); + +describe('Tooltip 비동기 데이터 테스트', () => { + test('Tooltip이 비동기 데이터로 업데이트된다.', async () => { + const fetchTooltipContent = (): Promise => + new Promise((resolve) => + setTimeout(() => resolve('Fetched Content'), 500), + ); + + const AsyncTooltip = () => { + const [content, setContent] = useState('Loading...'); + + useEffect(() => { + fetchTooltipContent().then((data) => setContent(data as string)); + }, []); + + return ( + + + + ); + }; + + render(); + + const trigger = screen.getByText('Hover me'); + await userEvent.hover(trigger); + + expect(screen.getByText('Loading...')).toBeInTheDocument(); + + await new Promise((resolve) => setTimeout(resolve, 500)); + expect(screen.getByText('Fetched Content')).toBeInTheDocument(); }); });