useLocalStorageQuery.test.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
  2. import { act, renderHook, waitFor } from '@testing-library/react'
  3. import React from 'react'
  4. import { afterEach, beforeEach, describe, expect, it } from 'vitest'
  5. import { useLocalStorageQuery } from '../useLocalStorage'
  6. import { customRenderHook } from '@/tests/lib/custom-render'
  7. function makeQueryClient() {
  8. return new QueryClient({ defaultOptions: { queries: { retry: false } } })
  9. }
  10. function makeWrapper(queryClient: QueryClient) {
  11. return ({ children }: { children: React.ReactNode }) =>
  12. React.createElement(QueryClientProvider, { client: queryClient }, children)
  13. }
  14. describe('useLocalStorageQuery', () => {
  15. beforeEach(() => {
  16. window.localStorage.clear()
  17. })
  18. afterEach(() => {
  19. window.localStorage.clear()
  20. })
  21. it('returns initialValue when localStorage has no entry for the key', async () => {
  22. const { result } = customRenderHook(() => useLocalStorageQuery('test-key', 'default'))
  23. await waitFor(() => expect(result.current[2].isSuccess).toBe(true))
  24. expect(result.current[0]).toBe('default')
  25. })
  26. it('returns the stored value from localStorage on mount', async () => {
  27. window.localStorage.setItem('test-key', JSON.stringify('persisted'))
  28. const { result } = customRenderHook(() => useLocalStorageQuery('test-key', 'default'))
  29. await waitFor(() => expect(result.current[0]).toBe('persisted'))
  30. })
  31. it('updates the value in state and localStorage when setValue is called', async () => {
  32. const { result } = customRenderHook(() => useLocalStorageQuery('test-key', 'default'))
  33. await waitFor(() => expect(result.current[2].isSuccess).toBe(true))
  34. act(() => {
  35. result.current[1]('updated')
  36. })
  37. await waitFor(() => expect(result.current[0]).toBe('updated'))
  38. expect(window.localStorage.getItem('test-key')).toBe(JSON.stringify('updated'))
  39. })
  40. it('supports a function updater that receives the current value', async () => {
  41. window.localStorage.setItem('count', JSON.stringify(5))
  42. const { result } = customRenderHook(() => useLocalStorageQuery('count', 0))
  43. await waitFor(() => expect(result.current[0]).toBe(5))
  44. act(() => {
  45. result.current[1]((prev: number) => prev + 1)
  46. })
  47. await waitFor(() => expect(result.current[0]).toBe(6))
  48. expect(window.localStorage.getItem('count')).toBe(JSON.stringify(6))
  49. })
  50. it('works with object values', async () => {
  51. const stored = { count: 3, label: 'hello' }
  52. window.localStorage.setItem('obj-key', JSON.stringify(stored))
  53. const { result } = customRenderHook(() =>
  54. useLocalStorageQuery('obj-key', { count: 0, label: '' })
  55. )
  56. await waitFor(() => expect(result.current[0]).toEqual(stored))
  57. })
  58. it('syncs two hooks sharing the same key via the same QueryClient', async () => {
  59. const queryClient = makeQueryClient()
  60. const { result } = renderHook(
  61. () => ({
  62. a: useLocalStorageQuery('shared-key', 'initial'),
  63. b: useLocalStorageQuery('shared-key', 'initial'),
  64. }),
  65. { wrapper: makeWrapper(queryClient) }
  66. )
  67. await waitFor(() => expect(result.current.a[2].isSuccess).toBe(true))
  68. act(() => {
  69. result.current.a[1]('from-a')
  70. })
  71. await waitFor(() => {
  72. expect(result.current.a[0]).toBe('from-a')
  73. expect(result.current.b[0]).toBe('from-a')
  74. })
  75. })
  76. it('uses the initialValue as fallback when the function updater runs before any stored value', async () => {
  77. const { result } = customRenderHook(() => useLocalStorageQuery('new-key', 10))
  78. await waitFor(() => expect(result.current[2].isSuccess).toBe(true))
  79. act(() => {
  80. result.current[1]((prev: number) => prev * 2)
  81. })
  82. await waitFor(() => expect(result.current[0]).toBe(20))
  83. expect(window.localStorage.getItem('new-key')).toBe(JSON.stringify(20))
  84. })
  85. })