aboutsummaryrefslogtreecommitdiffstats
path: root/frontend/src/Navigation.test.tsx
blob: 7d7324982805953ec5efdfee2e9f2fa3cd15ffe1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import React from 'react';
import App from './App';
import '@testing-library/jest-dom';

describe('Navigation and Filtering', () => {
    beforeEach(() => {
        vi.resetAllMocks();
        global.fetch = vi.fn();
        // Default mock response for auth
        vi.mocked(global.fetch).mockImplementation((url) => {
            const urlStr = url.toString();
            if (urlStr.includes('/api/auth')) return Promise.resolve({ ok: true, json: async () => ({ status: 'ok' }) } as Response);
            if (urlStr.includes('/api/feed/')) return Promise.resolve({
                ok: true,
                json: async () => [
                    { _id: 1, title: 'Feed 1', url: 'http://f1.com' },
                    { _id: 2, title: 'Feed 2', url: 'http://f2.com' }
                ]
            } as Response);
            if (urlStr.includes('/api/tag')) return Promise.resolve({ ok: true, json: async () => [] } as Response);
            if (urlStr.includes('/api/stream')) return Promise.resolve({ ok: true, json: async () => [] } as Response);
            return Promise.resolve({ ok: true, json: async () => ({}) } as Response);
        });
    });

    it('preserves "all" filter when clicking a feed', async () => {
        Object.defineProperty(window, 'innerWidth', { writable: true, configurable: true, value: 1024 });
        window.history.pushState({}, '', '/');
        render(<App />);

        // Wait for sidebar to load and feeds section to be visible
        await waitFor(() => {
            expect(screen.queryByText(/Loading feeds/i)).not.toBeInTheDocument();
        });

        // Expand feeds if not expanded
        const feedsHeader = await screen.findByRole('heading', { name: /Feeds/i, level: 4 });
        fireEvent.click(feedsHeader);

        await waitFor(() => {
            expect(screen.getByText('Feed 1')).toBeInTheDocument();
        });
        // Click 'all' filter
        const allFilter = screen.getByText('all');
        fireEvent.click(allFilter);

        // Verify URL has filter=all
        expect(window.location.search).toContain('filter=all');

        // Click Feed 1
        const feed1Link = screen.getByText('Feed 1');
        fireEvent.click(feed1Link);

        // Verify URL is /feed/1?filter=all (or similar)
        await waitFor(() => {
            expect(window.location.pathname).toContain('/feed/1');
            expect(window.location.search).toContain('filter=all');
        });

        // Click Feed 2
        const feed2Link = screen.getByText('Feed 2');
        fireEvent.click(feed2Link);

        // Verify URL is /feed/2?filter=all
        await waitFor(() => {
            expect(window.location.pathname).toContain('/feed/2');
            expect(window.location.search).toContain('filter=all');
        });
    });

    it('highlights the correct filter link', async () => {
        window.history.pushState({}, '', '/');
        render(<App />);

        await waitFor(() => {
            expect(screen.getByText('unread')).toHaveClass('active');
        });

        fireEvent.click(screen.getByText('all'));
        await waitFor(() => {
            expect(screen.getByText('all')).toHaveClass('active');
            expect(screen.getByText('unread')).not.toHaveClass('active');
        });
    });

    it('highlights "unread" as active even when on a feed page without filter param', async () => {
        window.history.pushState({}, '', '/feed/1');
        render(<App />);

        await waitFor(() => {
            expect(screen.getByText('unread')).toHaveClass('active');
        });
    });

    it('preserves search query when clicking a feed', async () => {
        window.history.pushState({}, '', '/?q=linux');
        render(<App />);

        await screen.findByRole('heading', { name: /Feeds/i, level: 4 });
        fireEvent.click(screen.getByRole('heading', { name: /Feeds/i, level: 4 }));

        await screen.findByText('Feed 1');
        fireEvent.click(screen.getByText('Feed 1'));

        await waitFor(() => {
            expect(window.location.pathname).toContain('/feed/1');
            expect(window.location.search).toContain('q=linux');
        });
    });
});