diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-13 07:13:18 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-13 07:13:18 -0800 |
| commit | a5cd9538b0db731a0d0e10e58804ef8ad32211b7 (patch) | |
| tree | 0b46b0f43965de9df03b023abc40d869224e38d8 /frontend/src/components/Settings.test.tsx | |
| parent | e31b68197ec16d2805ec14c2bf532a03f4739e92 (diff) | |
| download | neko-a5cd9538b0db731a0d0e10e58804ef8ad32211b7.tar.gz neko-a5cd9538b0db731a0d0e10e58804ef8ad32211b7.tar.bz2 neko-a5cd9538b0db731a0d0e10e58804ef8ad32211b7.zip | |
Implement Frontend Settings with tests
Diffstat (limited to 'frontend/src/components/Settings.test.tsx')
| -rw-r--r-- | frontend/src/components/Settings.test.tsx | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/frontend/src/components/Settings.test.tsx b/frontend/src/components/Settings.test.tsx new file mode 100644 index 0000000..a15192d --- /dev/null +++ b/frontend/src/components/Settings.test.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import '@testing-library/jest-dom'; +import { render, screen, waitFor, fireEvent } from '@testing-library/react'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import Settings from './Settings'; + +describe('Settings Component', () => { + beforeEach(() => { + vi.resetAllMocks(); + global.fetch = vi.fn(); + // Mock confirm + global.confirm = vi.fn(() => true); + }); + + it('renders feed list', async () => { + const mockFeeds = [ + { _id: 1, title: 'Tech News', url: 'http://tech.com/rss', category: 'tech' }, + { _id: 2, title: 'Gaming', url: 'http://gaming.com/rss', category: 'gaming' }, + ]; + + (global.fetch as any).mockResolvedValueOnce({ + ok: true, + json: async () => mockFeeds, + }); + + render(<Settings />); + + await waitFor(() => { + expect(screen.getByText('Tech News')).toBeInTheDocument(); + expect(screen.getByText('http://tech.com/rss')).toBeInTheDocument(); + expect(screen.getByText('Gaming')).toBeInTheDocument(); + }); + }); + + it('adds a new feed', async () => { + (global.fetch as any) + .mockResolvedValueOnce({ ok: true, json: async () => [] }) // Initial load + .mockResolvedValueOnce({ ok: true, json: async () => ({}) }) // Add feed + .mockResolvedValueOnce({ ok: true, json: async () => [{ _id: 3, title: 'New Feed', url: 'http://new.com/rss' }] }); // Refresh load + + render(<Settings />); + + // Wait for initial load to finish + await waitFor(() => { + expect(screen.queryByText('Loading...')).not.toBeInTheDocument(); + }); + + const input = screen.getByPlaceholderText('https://example.com/feed.xml'); + const button = screen.getByText('Add Feed'); + + fireEvent.change(input, { target: { value: 'http://new.com/rss' } }); + fireEvent.click(button); + + await waitFor(() => { + expect(global.fetch).toHaveBeenCalledWith('/api/feed/', expect.objectContaining({ + method: 'POST', + body: JSON.stringify({ url: 'http://new.com/rss' }), + })); + }); + + // Wait for refresh + await waitFor(() => { + expect(screen.getByText('New Feed')).toBeInTheDocument(); + }); + }); + + it('deletes a feed', async () => { + const mockFeeds = [ + { _id: 1, title: 'Tech News', url: 'http://tech.com/rss', category: 'tech' }, + ]; + + (global.fetch as any) + .mockResolvedValueOnce({ ok: true, json: async () => mockFeeds }) // Initial load + .mockResolvedValueOnce({ ok: true }); // Delete + + render(<Settings />); + + await waitFor(() => { + expect(screen.queryByText('Loading...')).not.toBeInTheDocument(); + expect(screen.getByText('Tech News')).toBeInTheDocument(); + }); + + const deleteBtn = screen.getByTitle('Delete Feed'); + fireEvent.click(deleteBtn); + + await waitFor(() => { + expect(global.confirm).toHaveBeenCalled(); + expect(global.fetch).toHaveBeenCalledWith('/api/feed/1', expect.objectContaining({ method: 'DELETE' })); + expect(screen.queryByText('Tech News')).not.toBeInTheDocument(); + }); + }); +}); |
