|
1 | 1 | import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' |
2 | 2 | import { render, screen, waitFor } from '@testing-library/react' |
| 3 | +import userEvent from '@testing-library/user-event' |
3 | 4 | import App from './App' |
4 | 5 | import { useStore } from './store' |
5 | 6 |
|
@@ -261,4 +262,78 @@ describe('App', () => { |
261 | 262 | }) |
262 | 263 | }) |
263 | 264 | }) |
| 265 | + |
| 266 | + describe('URL sync behavior', () => { |
| 267 | + let replaceStateMock: ReturnType<typeof vi.fn> |
| 268 | + |
| 269 | + beforeEach(() => { |
| 270 | + replaceStateMock = vi.fn() |
| 271 | + vi.spyOn(window.history, 'replaceState').mockImplementation(replaceStateMock) |
| 272 | + |
| 273 | + useStore.setState({ |
| 274 | + apiKey: 'test-api-key', |
| 275 | + isValid: true, |
| 276 | + isValidating: false, |
| 277 | + }) |
| 278 | + }) |
| 279 | + |
| 280 | + it('should NOT update URL when filter input changes', async () => { |
| 281 | + const user = userEvent.setup() |
| 282 | + render(<App />) |
| 283 | + |
| 284 | + // Clear any calls from initialization |
| 285 | + replaceStateMock.mockClear() |
| 286 | + |
| 287 | + // Type into the whiteboard filter |
| 288 | + const whiteboardInput = screen.getByLabelText(/whiteboard/i) |
| 289 | + await user.clear(whiteboardInput) |
| 290 | + await user.type(whiteboardInput, '[test]') |
| 291 | + |
| 292 | + // URL should NOT have been updated |
| 293 | + expect(replaceStateMock).not.toHaveBeenCalled() |
| 294 | + }) |
| 295 | + |
| 296 | + it('should update URL when Apply Filter button is clicked', async () => { |
| 297 | + const user = userEvent.setup() |
| 298 | + const fetchBugs = vi.fn().mockResolvedValue([]) |
| 299 | + useStore.setState({ |
| 300 | + fetchBugs, |
| 301 | + filters: { |
| 302 | + whiteboardTag: '[test]', |
| 303 | + component: '', |
| 304 | + excludeMetaBugs: false, |
| 305 | + sortOrder: 'priority', |
| 306 | + }, |
| 307 | + }) |
| 308 | + |
| 309 | + render(<App />) |
| 310 | + |
| 311 | + // Clear any calls from initialization |
| 312 | + replaceStateMock.mockClear() |
| 313 | + |
| 314 | + // Click Apply Filters button |
| 315 | + const applyButton = screen.getByRole('button', { name: /apply filters/i }) |
| 316 | + await user.click(applyButton) |
| 317 | + |
| 318 | + // URL should have been updated with the filter |
| 319 | + expect(replaceStateMock).toHaveBeenCalled() |
| 320 | + const call = replaceStateMock.mock.calls[0] |
| 321 | + expect(call[2]).toContain('whiteboard=%5Btest%5D') |
| 322 | + }) |
| 323 | + |
| 324 | + it('should NOT update URL when sort order changes without clicking Apply', async () => { |
| 325 | + const user = userEvent.setup() |
| 326 | + render(<App />) |
| 327 | + |
| 328 | + // Clear any calls from initialization |
| 329 | + replaceStateMock.mockClear() |
| 330 | + |
| 331 | + // Change sort order by clicking the radio button |
| 332 | + const lastChangedRadio = screen.getByRole('radio', { name: /last changed/i }) |
| 333 | + await user.click(lastChangedRadio) |
| 334 | + |
| 335 | + // URL should NOT have been updated |
| 336 | + expect(replaceStateMock).not.toHaveBeenCalled() |
| 337 | + }) |
| 338 | + }) |
264 | 339 | }) |
0 commit comments