diff --git a/packages/web/src/app/cli-auth/client.test.tsx b/packages/web/src/app/cli-auth/client.test.tsx new file mode 100644 index 00000000..2552b0e2 --- /dev/null +++ b/packages/web/src/app/cli-auth/client.test.tsx @@ -0,0 +1,115 @@ +/** @vitest-environment jsdom */ +import React from 'react'; +import { render, screen, fireEvent, act } from '@testing-library/react'; +import { describe, it, expect, vi, beforeEach, afterEach, type Mock } from 'vitest'; +import { CliAuthClient } from './client'; +import { cleanup } from '@testing-library/react'; + +// Setup React globally +global.React = React; + +describe('CliAuthClient', () => { + beforeEach(() => { + vi.clearAllMocks(); + global.fetch = vi.fn(); + }); + + afterEach(() => { + cleanup(); + }); + + const defaultProps = { + state: 'mock-state', + userName: 'Test User', + userEmail: 'test@example.com', + argosToken: 'mock-token' + }; + + it('renders correctly in pending state', () => { + render(); + expect(screen.getByText('CLI 로그인 요청')).toBeDefined(); + expect(screen.getByText('Test User')).toBeDefined(); + expect(screen.getByRole('button', { name: '허용' })).toBeDefined(); + expect(screen.getByRole('button', { name: '거부' })).toBeDefined(); + }); + + it('handles allow click successfully', async () => { + (global.fetch as Mock).mockResolvedValue({ ok: true }); + + render(); + + const allowBtn = screen.getByRole('button', { name: '허용' }); + + await act(async () => { + fireEvent.click(allowBtn); + }); + + expect(global.fetch).toHaveBeenCalledWith('/api/auth/cli-callback', expect.objectContaining({ + method: 'POST', + body: JSON.stringify({ state: 'mock-state' }) + })); + + expect(screen.getByText('로그인 완료')).toBeDefined(); + }); + + it('handles allow click failure', async () => { + (global.fetch as Mock).mockResolvedValue({ ok: false }); + + render(); + + const allowBtn = screen.getByRole('button', { name: '허용' }); + + await act(async () => { + fireEvent.click(allowBtn); + }); + + expect(screen.getByText('오류 발생')).toBeDefined(); + }); + + it('handles deny click successfully', async () => { + (global.fetch as Mock).mockResolvedValue({ ok: true }); + + render(); + + const denyBtn = screen.getByRole('button', { name: '거부' }); + + await act(async () => { + fireEvent.click(denyBtn); + }); + + expect(global.fetch).toHaveBeenCalledWith('/api/auth/cli-callback', expect.objectContaining({ + method: 'POST', + body: JSON.stringify({ state: 'mock-state', denied: true }) + })); + + expect(screen.getByText('로그인 거부됨')).toBeDefined(); + }); + + it('handles network error', async () => { + (global.fetch as Mock).mockRejectedValue(new Error('Network error')); + + render(); + + const allowBtn = screen.getByRole('button', { name: '허용' }); + + await act(async () => { + fireEvent.click(allowBtn); + }); + + expect(screen.getByText('오류 발생')).toBeDefined(); + }); + + it('handles deny click with network error', async () => { + (global.fetch as Mock).mockRejectedValue(new Error('Network error')); + + render(); + + const denyBtn = screen.getByRole('button', { name: '거부' }); + + await act(async () => { + fireEvent.click(denyBtn); + }); + + expect(screen.getByText('오류 발생')).toBeDefined(); + }); +}); diff --git a/packages/web/src/app/cli-auth/client.tsx b/packages/web/src/app/cli-auth/client.tsx index 77714162..8bd1c63d 100644 --- a/packages/web/src/app/cli-auth/client.tsx +++ b/packages/web/src/app/cli-auth/client.tsx @@ -2,6 +2,8 @@ import { useState } from 'react' +import { Button } from '@/components/ui/button' + interface Props { state: string userName: string @@ -92,20 +94,21 @@ export function CliAuthClient({ state, userName, userEmail, argosToken }: Props)
- - +
)