useQueryParamState.spec.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import {LocationFixture} from 'sentry-fixture/locationFixture';
  2. import {act, renderHook} from 'sentry-test/reactTestingLibrary';
  3. import type {Sort} from 'sentry/utils/discover/fields';
  4. import {decodeSorts} from 'sentry/utils/queryString';
  5. import {useLocation} from 'sentry/utils/useLocation';
  6. import {useNavigate} from 'sentry/utils/useNavigate';
  7. import {UrlParamBatchProvider} from 'sentry/views/dashboards/widgetBuilder/contexts/urlParamBatchContext';
  8. import {useQueryParamState} from 'sentry/views/dashboards/widgetBuilder/hooks/useQueryParamState';
  9. import {formatSort} from 'sentry/views/explore/contexts/pageParamsContext/sortBys';
  10. jest.mock('sentry/utils/useLocation');
  11. jest.mock('sentry/utils/useNavigate');
  12. const mockedUseLocation = jest.mocked(useLocation);
  13. const mockedUseNavigate = jest.mocked(useNavigate);
  14. describe('useQueryParamState', () => {
  15. beforeEach(() => {
  16. jest.useFakeTimers();
  17. });
  18. afterEach(() => {
  19. jest.useRealTimers();
  20. });
  21. it('should get the initial value from the query param', () => {
  22. mockedUseLocation.mockReturnValue(
  23. LocationFixture({query: {testField: 'initial state'}})
  24. );
  25. const {result} = renderHook(() => useQueryParamState({fieldName: 'testField'}), {
  26. wrapper: UrlParamBatchProvider,
  27. });
  28. expect(result.current[0]).toBe('initial state');
  29. });
  30. it('should update the local state and the query param', () => {
  31. const mockedNavigate = jest.fn();
  32. mockedUseNavigate.mockReturnValue(mockedNavigate);
  33. const {result} = renderHook(() => useQueryParamState({fieldName: 'testField'}), {
  34. wrapper: UrlParamBatchProvider,
  35. });
  36. act(() => {
  37. result.current[1]('newValue');
  38. });
  39. // The local state should be updated
  40. expect(result.current[0]).toBe('newValue');
  41. // The query param should not be updated yet
  42. expect(mockedNavigate).not.toHaveBeenCalledWith({
  43. ...LocationFixture(),
  44. query: {testField: 'initial state'},
  45. });
  46. // Run the timers to trigger queued updates
  47. jest.runAllTimers();
  48. // The query param should be updated
  49. expect(mockedNavigate).toHaveBeenCalledWith({
  50. ...LocationFixture(),
  51. query: {testField: 'newValue'},
  52. });
  53. // The local state should be still reflect the new value
  54. expect(result.current[0]).toBe('newValue');
  55. });
  56. it('should use the decoder function to decode the query param value if provided', () => {
  57. mockedUseLocation.mockReturnValue(
  58. LocationFixture({query: {testField: 'initial state'}})
  59. );
  60. const testDeserializer = (value: string) => `${value.toUpperCase()} - decoded`;
  61. const {result} = renderHook(
  62. () => useQueryParamState({fieldName: 'testField', deserializer: testDeserializer}),
  63. {
  64. wrapper: UrlParamBatchProvider,
  65. }
  66. );
  67. expect(result.current[0]).toBe('INITIAL STATE - decoded');
  68. });
  69. it('can take any kind of value and serialize it to a string compatible with query params', () => {
  70. type TestType = {
  71. count: number;
  72. isActive: boolean;
  73. value: string;
  74. };
  75. const mockedNavigate = jest.fn();
  76. mockedUseNavigate.mockReturnValue(mockedNavigate);
  77. const testSerializer = (value: TestType) =>
  78. `${value.value} - ${value.count} - ${value.isActive}`;
  79. const {result} = renderHook(
  80. () => useQueryParamState({fieldName: 'testField', serializer: testSerializer}),
  81. {
  82. wrapper: UrlParamBatchProvider,
  83. }
  84. );
  85. act(() => {
  86. result.current[1]({value: 'newValue', count: 2, isActive: true});
  87. });
  88. expect(mockedNavigate).toHaveBeenCalledWith({
  89. ...LocationFixture(),
  90. query: {testField: 'newValue - 2 - true'},
  91. });
  92. });
  93. it('can decode and update sorts', () => {
  94. mockedUseLocation.mockReturnValue(LocationFixture({query: {sort: '-testField'}}));
  95. const mockedNavigate = jest.fn();
  96. mockedUseNavigate.mockReturnValue(mockedNavigate);
  97. const {result} = renderHook(
  98. () =>
  99. useQueryParamState<Sort[]>({
  100. fieldName: 'sort',
  101. decoder: decodeSorts,
  102. serializer: value => value.map(formatSort),
  103. }),
  104. {
  105. wrapper: UrlParamBatchProvider,
  106. }
  107. );
  108. expect(result.current[0]).toEqual([{field: 'testField', kind: 'desc'}]);
  109. act(() => {
  110. result.current[1]([{field: 'testField', kind: 'asc'}]);
  111. });
  112. expect(mockedNavigate).toHaveBeenCalledWith({
  113. ...LocationFixture(),
  114. query: {sort: ['testField']},
  115. });
  116. });
  117. });