useLocationQuery.spec.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import type {Location} from 'history';
  2. import {reactHooks} from 'sentry-test/reactTestingLibrary';
  3. import {decodeInteger, decodeList, decodeScalar} from 'sentry/utils/queryString';
  4. import useLocationQuery from 'sentry/utils/url/useLocationQuery';
  5. import {useLocation} from 'sentry/utils/useLocation';
  6. jest.mock('sentry/utils/useLocation');
  7. const mockLocation: Location = {
  8. key: '',
  9. search: '',
  10. hash: '',
  11. action: 'PUSH',
  12. state: null,
  13. query: {},
  14. pathname: '/mock-pathname/',
  15. };
  16. describe('useLocationQuery', () => {
  17. it('should read query values from the url', () => {
  18. jest.mocked(useLocation).mockReturnValue({
  19. ...mockLocation,
  20. query: {
  21. name: 'Adam',
  22. age: '12',
  23. titles: ['Mr.', 'Dr.'],
  24. extra: 'foo bar',
  25. },
  26. } as Location);
  27. const {result} = reactHooks.renderHook(useLocationQuery, {
  28. initialProps: {
  29. fields: {
  30. name: decodeScalar,
  31. age: decodeInteger,
  32. titles: decodeList,
  33. },
  34. },
  35. });
  36. expect(result.current).toStrictEqual({
  37. name: 'Adam',
  38. age: 12,
  39. titles: ['Mr.', 'Dr.'],
  40. });
  41. });
  42. it('should return undefined if the url does not contain a requested field', () => {
  43. jest.mocked(useLocation).mockReturnValue({
  44. ...mockLocation,
  45. query: {},
  46. } as Location);
  47. const {result} = reactHooks.renderHook(useLocationQuery, {
  48. initialProps: {
  49. fields: {
  50. name: decodeScalar,
  51. age: decodeInteger,
  52. titles: decodeList,
  53. },
  54. },
  55. });
  56. expect(result.current).toStrictEqual({
  57. name: '',
  58. age: 0,
  59. titles: [],
  60. });
  61. });
  62. it('should pass-through static values along with decoded ones', () => {
  63. jest.mocked(useLocation).mockReturnValueOnce({
  64. ...mockLocation,
  65. query: {
  66. name: 'Adam',
  67. titles: ['Mr.', 'Dr.'],
  68. },
  69. } as Location);
  70. const {result} = reactHooks.renderHook(useLocationQuery, {
  71. initialProps: {
  72. fields: {
  73. name: decodeScalar,
  74. stringy: 'bar',
  75. list: ['biz', 'baz'],
  76. num: 12,
  77. },
  78. },
  79. });
  80. expect(result.current).toStrictEqual({
  81. name: 'Adam',
  82. stringy: 'bar',
  83. list: ['biz', 'baz'],
  84. num: 12,
  85. });
  86. });
  87. it('should only change return object identity when values change', () => {
  88. // 1st render:
  89. jest.mocked(useLocation).mockReturnValueOnce({
  90. ...mockLocation,
  91. query: {
  92. name: 'Adam',
  93. titles: ['Mr.', 'Dr.'],
  94. },
  95. } as Location);
  96. // 2nd render, same values (but the array is re-built, new object ref):
  97. jest.mocked(useLocation).mockReturnValueOnce({
  98. ...mockLocation,
  99. query: {
  100. name: 'Adam',
  101. titles: ['Mr.', 'Dr.'],
  102. },
  103. } as Location);
  104. // 3rd render, name is changed.
  105. jest.mocked(useLocation).mockReturnValueOnce({
  106. ...mockLocation,
  107. query: {
  108. name: 'Betty',
  109. },
  110. } as Location);
  111. const {result, rerender} = reactHooks.renderHook(useLocationQuery, {
  112. initialProps: {
  113. fields: {
  114. name: decodeScalar,
  115. age: decodeInteger,
  116. titles: decodeList,
  117. },
  118. },
  119. });
  120. const first = result.current;
  121. rerender();
  122. const second = result.current;
  123. rerender();
  124. const third = result.current;
  125. expect(first.name).toBe('Adam');
  126. expect(second.name).toBe('Adam');
  127. expect(third.name).toBe('Betty');
  128. // Must be strict object equality:
  129. expect(first).toBe(second);
  130. // Object has changed because a value has changed:
  131. expect(first).not.toBe(third);
  132. });
  133. });