|
1 | | -import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; |
| 1 | +import { render, screen, waitFor } from '@testing-library/react'; |
2 | 2 | import userEvent from '@testing-library/user-event'; |
3 | 3 | import ProblemsPage from '@/app/problems/page'; |
4 | 4 | import { ProgressContext } from '@/components/ProgressProvider'; |
@@ -33,7 +33,7 @@ jest.mock('next/link', () => { |
33 | 33 | }); |
34 | 34 |
|
35 | 35 | // Mock the problems data |
36 | | -const mockProblems = [ |
| 36 | +const _mockProblems = [ |
37 | 37 | { |
38 | 38 | id: 'problem-1', |
39 | 39 | title: 'Array Destructuring', |
@@ -145,34 +145,54 @@ jest.mock('@/components/FilterSidebar', () => { |
145 | 145 | <div data-testid="problem-counts"> |
146 | 146 | {problemCounts.easy}/{problemCounts.medium}/{problemCounts.hard}/{problemCounts.total} |
147 | 147 | </div> |
148 | | - <button onClick={() => onDifficultyChange('easy')} data-testid="filter-easy"> |
| 148 | + <button type="button" onClick={() => onDifficultyChange('easy')} data-testid="filter-easy"> |
149 | 149 | Filter Easy |
150 | 150 | </button> |
151 | | - <button onClick={() => onDifficultyChange('medium')} data-testid="filter-medium"> |
| 151 | + <button |
| 152 | + type="button" |
| 153 | + onClick={() => onDifficultyChange('medium')} |
| 154 | + data-testid="filter-medium" |
| 155 | + > |
152 | 156 | Filter Medium |
153 | 157 | </button> |
154 | | - <button onClick={() => onDifficultyChange('hard')} data-testid="filter-hard"> |
| 158 | + <button type="button" onClick={() => onDifficultyChange('hard')} data-testid="filter-hard"> |
155 | 159 | Filter Hard |
156 | 160 | </button> |
157 | | - <button onClick={() => onDifficultyChange('all')} data-testid="filter-all-difficulty"> |
| 161 | + <button |
| 162 | + type="button" |
| 163 | + onClick={() => onDifficultyChange('all')} |
| 164 | + data-testid="filter-all-difficulty" |
| 165 | + > |
158 | 166 | All Difficulties |
159 | 167 | </button> |
160 | | - <button onClick={() => onCategoryChange('TypeScript')} data-testid="filter-typescript"> |
| 168 | + <button |
| 169 | + type="button" |
| 170 | + onClick={() => onCategoryChange('TypeScript')} |
| 171 | + data-testid="filter-typescript" |
| 172 | + > |
161 | 173 | Filter TypeScript |
162 | 174 | </button> |
163 | | - <button onClick={() => onCategoryChange('all')} data-testid="filter-all-category"> |
| 175 | + <button |
| 176 | + type="button" |
| 177 | + onClick={() => onCategoryChange('all')} |
| 178 | + data-testid="filter-all-category" |
| 179 | + > |
164 | 180 | All Categories |
165 | 181 | </button> |
166 | | - <button onClick={() => onStatusChange('solved')} data-testid="filter-solved"> |
| 182 | + <button type="button" onClick={() => onStatusChange('solved')} data-testid="filter-solved"> |
167 | 183 | Filter Solved |
168 | 184 | </button> |
169 | | - <button onClick={() => onStatusChange('unsolved')} data-testid="filter-unsolved"> |
| 185 | + <button |
| 186 | + type="button" |
| 187 | + onClick={() => onStatusChange('unsolved')} |
| 188 | + data-testid="filter-unsolved" |
| 189 | + > |
170 | 190 | Filter Unsolved |
171 | 191 | </button> |
172 | | - <button onClick={() => onStatusChange('all')} data-testid="filter-all-status"> |
| 192 | + <button type="button" onClick={() => onStatusChange('all')} data-testid="filter-all-status"> |
173 | 193 | All Status |
174 | 194 | </button> |
175 | | - <button onClick={onClearFilters} data-testid="clear-filters"> |
| 195 | + <button type="button" onClick={onClearFilters} data-testid="clear-filters"> |
176 | 196 | Clear Filters |
177 | 197 | </button> |
178 | 198 | </div> |
@@ -888,3 +908,92 @@ describe('ProblemsPage Memoization', () => { |
888 | 908 | }); |
889 | 909 | }); |
890 | 910 | }); |
| 911 | + |
| 912 | +describe('ProblemsPage Mobile Filter Callbacks', () => { |
| 913 | + beforeEach(() => { |
| 914 | + mockSearchParams.delete('category'); |
| 915 | + mockGet.mockClear(); |
| 916 | + }); |
| 917 | + |
| 918 | + it('should apply difficulty filter through mobile filter sidebar', async () => { |
| 919 | + const user = userEvent.setup(); |
| 920 | + renderWithProgress(<ProblemsPage />); |
| 921 | + |
| 922 | + // Toggle mobile filters to show them |
| 923 | + await waitFor(() => { |
| 924 | + expect(screen.getByRole('button', { name: /^Filters$/i })).toBeInTheDocument(); |
| 925 | + }); |
| 926 | + |
| 927 | + const filterButton = screen.getByRole('button', { name: /^Filters$/i }); |
| 928 | + await user.click(filterButton); |
| 929 | + |
| 930 | + // Wait for mobile sidebar to appear (should be the second one) |
| 931 | + await waitFor(() => { |
| 932 | + const sidebars = screen.getAllByTestId('filter-sidebar'); |
| 933 | + expect(sidebars.length).toBe(2); |
| 934 | + }); |
| 935 | + |
| 936 | + // Get the mobile sidebar's easy filter (should be the second one) |
| 937 | + const easyButtons = screen.getAllByTestId('filter-easy'); |
| 938 | + // The second filter-easy should be in the mobile sidebar |
| 939 | + await user.click(easyButtons[1]); |
| 940 | + |
| 941 | + await waitFor(() => { |
| 942 | + const problemCount = screen.getByTestId('problem-count'); |
| 943 | + expect(problemCount).toHaveTextContent('2'); |
| 944 | + }); |
| 945 | + }); |
| 946 | + |
| 947 | + it('should apply category filter through mobile filter sidebar', async () => { |
| 948 | + const user = userEvent.setup(); |
| 949 | + renderWithProgress(<ProblemsPage />); |
| 950 | + |
| 951 | + // Toggle mobile filters |
| 952 | + const filterButton = screen.getByRole('button', { name: /^Filters$/i }); |
| 953 | + await user.click(filterButton); |
| 954 | + |
| 955 | + // Wait for mobile sidebar |
| 956 | + await waitFor(() => { |
| 957 | + const sidebars = screen.getAllByTestId('filter-sidebar'); |
| 958 | + expect(sidebars.length).toBe(2); |
| 959 | + }); |
| 960 | + |
| 961 | + // Use the mobile sidebar's category filter (second one) |
| 962 | + const typeScriptButtons = screen.getAllByTestId('filter-typescript'); |
| 963 | + await user.click(typeScriptButtons[1]); |
| 964 | + |
| 965 | + await waitFor(() => { |
| 966 | + const problemCount = screen.getByTestId('problem-count'); |
| 967 | + expect(problemCount).toHaveTextContent('2'); |
| 968 | + }); |
| 969 | + }); |
| 970 | + |
| 971 | + it('should apply status filter through mobile filter sidebar', async () => { |
| 972 | + const user = userEvent.setup(); |
| 973 | + const isSolved = jest.fn((id: string) => ['problem-1', 'problem-2'].includes(id)); |
| 974 | + |
| 975 | + renderWithProgress(<ProblemsPage />, { |
| 976 | + isSolved, |
| 977 | + solvedCount: 2, |
| 978 | + }); |
| 979 | + |
| 980 | + // Toggle mobile filters |
| 981 | + const filterButton = screen.getByRole('button', { name: /^Filters$/i }); |
| 982 | + await user.click(filterButton); |
| 983 | + |
| 984 | + // Wait for mobile sidebar |
| 985 | + await waitFor(() => { |
| 986 | + const sidebars = screen.getAllByTestId('filter-sidebar'); |
| 987 | + expect(sidebars.length).toBe(2); |
| 988 | + }); |
| 989 | + |
| 990 | + // Use the mobile sidebar's status filter (second one) |
| 991 | + const solvedButtons = screen.getAllByTestId('filter-solved'); |
| 992 | + await user.click(solvedButtons[1]); |
| 993 | + |
| 994 | + await waitFor(() => { |
| 995 | + const problemCount = screen.getByTestId('problem-count'); |
| 996 | + expect(problemCount).toHaveTextContent('2'); |
| 997 | + }); |
| 998 | + }); |
| 999 | +}); |
0 commit comments