groupStore.spec.tsx 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import {ActorFixture} from 'sentry-fixture/actor';
  2. import {GroupFixture} from 'sentry-fixture/group';
  3. import {ProjectFixture} from 'sentry-fixture/project';
  4. import GroupStore from 'sentry/stores/groupStore';
  5. import type {TimeseriesValue} from 'sentry/types/core';
  6. import type {Group, GroupStats} from 'sentry/types/group';
  7. import {GroupActivityType} from 'sentry/types/group';
  8. const MOCK_PROJECT = ProjectFixture();
  9. const g = (id: string, params?: Partial<Group>): Group => {
  10. return GroupFixture({id, project: MOCK_PROJECT, ...params});
  11. };
  12. describe('GroupStore', function () {
  13. beforeEach(function () {
  14. GroupStore.reset();
  15. });
  16. describe('add()', function () {
  17. it('should add new entries', function () {
  18. GroupStore.items = [];
  19. GroupStore.add([g('1'), g('2')]);
  20. expect(GroupStore.items).toEqual([g('1'), g('2')]);
  21. });
  22. it('should update matching existing entries', function () {
  23. GroupStore.items = [g('1'), g('2')];
  24. GroupStore.add([g('1', {count: '1337'}), g('3')]);
  25. expect(GroupStore.getAllItemIds()).toEqual(['1', '2', '3']);
  26. expect(GroupStore.items[0]).toEqual(
  27. expect.objectContaining({id: '1', count: '1337'})
  28. );
  29. });
  30. it('should attempt to preserve order of ids', function () {
  31. GroupStore.add([g('2'), g('1'), g('3')]);
  32. expect(GroupStore.getAllItemIds()).toEqual(['2', '1', '3']);
  33. });
  34. });
  35. describe('addToFront()', function () {
  36. it('should add new entries to beginning of the list', function () {
  37. GroupStore.items = [g('2')];
  38. GroupStore.addToFront([g('1'), g('3')]);
  39. expect(GroupStore.items).toEqual([g('1'), g('3'), g('2')]);
  40. });
  41. it('should update matching existing entries', function () {
  42. GroupStore.items = [g('1'), g('2')];
  43. GroupStore.addToFront([g('1', {count: '1337'}), g('3')]);
  44. expect(GroupStore.getAllItems()).toEqual([
  45. expect.objectContaining({id: '1', count: '1337'}),
  46. g('3'),
  47. g('2'),
  48. ]);
  49. });
  50. it('should attempt to preserve order of ids', function () {
  51. GroupStore.addToFront([g('2'), g('1'), g('3')]);
  52. expect(GroupStore.getAllItemIds()).toEqual(['2', '1', '3']);
  53. });
  54. });
  55. describe('remove()', function () {
  56. it('should remove entry', function () {
  57. GroupStore.items = [g('1'), g('2')];
  58. GroupStore.remove(['1']);
  59. expect(GroupStore.items).toEqual([g('2')]);
  60. });
  61. it('should remove multiple entries', function () {
  62. GroupStore.items = [g('1'), g('2'), g('3')];
  63. GroupStore.remove(['1', '2']);
  64. expect(GroupStore.items).toEqual([g('3')]);
  65. });
  66. it('should not remove already removed item', function () {
  67. GroupStore.items = [g('1'), g('2')];
  68. GroupStore.remove(['0']);
  69. expect(GroupStore.items).toEqual([g('1'), g('2')]);
  70. });
  71. });
  72. describe('onMergeSuccess()', function () {
  73. it('should remove the non-parent merged ids', function () {
  74. GroupStore.items = [g('1'), g('2'), g('3'), g('4')];
  75. GroupStore.onMergeSuccess(
  76. '',
  77. ['2', '3', '4'], // items merged
  78. {merge: {parent: '3'}} // merge API response
  79. );
  80. expect(GroupStore.items).toEqual([
  81. g('1'),
  82. g('3'), // parent
  83. ]);
  84. });
  85. });
  86. describe('onPopulateStats()', function () {
  87. const stats: Record<string, TimeseriesValue[]> = {auto: [[1611576000, 10]]};
  88. beforeEach(function () {
  89. jest.spyOn(GroupStore, 'trigger');
  90. GroupStore.items = [g('1'), g('2'), g('3')];
  91. });
  92. afterEach(() => {
  93. jest.restoreAllMocks();
  94. });
  95. it('should merge stats into existing groups', function () {
  96. GroupStore.onPopulateStats(
  97. ['1', '2', '3'],
  98. [
  99. {id: '1', stats} as GroupStats,
  100. {id: '2', stats} as GroupStats,
  101. {id: '3', stats} as GroupStats,
  102. ]
  103. );
  104. const group = GroupStore.getAllItems()[0] as Group;
  105. expect(group.stats).toEqual(stats);
  106. expect(GroupStore.trigger).toHaveBeenCalledWith(new Set(['1', '2', '3']));
  107. });
  108. it('should not change current item ids', function () {
  109. GroupStore.onPopulateStats(
  110. ['2', '3'],
  111. [{id: '2', stats} as GroupStats, {id: '3', stats} as GroupStats]
  112. );
  113. const group1 = GroupStore.getAllItems()[0] as Group;
  114. const group2 = GroupStore.getAllItems()[1] as Group;
  115. expect(GroupStore.trigger).toHaveBeenCalledWith(new Set(['2', '3']));
  116. expect(group1.stats).not.toEqual(stats);
  117. expect(group2.stats).toEqual(stats);
  118. });
  119. });
  120. describe('getAllItems()', function () {
  121. it('Merges pending changes into items', function () {
  122. GroupStore.items = [];
  123. GroupStore.add([g('1'), g('2')]);
  124. GroupStore.onUpdate('1337', ['1'], {someChange: true});
  125. expect(GroupStore.get('1')).toEqual(
  126. expect.objectContaining({id: '1', someChange: true})
  127. );
  128. });
  129. });
  130. describe('getState()', function () {
  131. it('returns a stable reference', () => {
  132. GroupStore.add([g('1'), g('2')]);
  133. const state = GroupStore.getState();
  134. expect(Object.is(state, GroupStore.getState())).toBe(true);
  135. });
  136. });
  137. describe('update methods', function () {
  138. beforeEach(function () {
  139. jest.spyOn(GroupStore, 'trigger');
  140. GroupStore.items = [g('1'), g('2'), g('3')];
  141. });
  142. afterEach(() => {
  143. jest.restoreAllMocks();
  144. });
  145. describe('onUpdate()', function () {
  146. it("should treat undefined itemIds argument as 'all'", function () {
  147. GroupStore.onUpdate('1337', undefined, {});
  148. expect(GroupStore.trigger).toHaveBeenCalledTimes(1);
  149. expect(GroupStore.trigger).toHaveBeenCalledWith(new Set(['1', '2', '3']));
  150. });
  151. it('should apply optimistic updates', function () {
  152. GroupStore.items = [g('1'), g('2')];
  153. GroupStore.add([g('1'), g('2')]);
  154. // Resolve 2 issues
  155. const itemIds = ['1', '2'];
  156. const data = {status: 'resolved', statusDetails: {}};
  157. GroupStore.onUpdate('12345', itemIds, data);
  158. expect(GroupStore.pendingChanges).toEqual(new Map([['12345', {itemIds, data}]]));
  159. expect(GroupStore.get('1')).toEqual({...g('1'), ...data});
  160. expect(GroupStore.get('2')).toEqual({...g('2'), ...data});
  161. });
  162. });
  163. describe('onUpdateSuccess()', function () {
  164. it("should treat undefined itemIds argument as 'all'", function () {
  165. GroupStore.onUpdateSuccess('1337', undefined, {});
  166. expect(GroupStore.trigger).toHaveBeenCalledTimes(1);
  167. expect(GroupStore.trigger).toHaveBeenCalledWith(new Set(['1', '2', '3']));
  168. });
  169. });
  170. describe('onUpdateError()', function () {
  171. it("should treat undefined itemIds argument as 'all'", function () {
  172. GroupStore.onUpdateError('1337', undefined, false);
  173. expect(GroupStore.trigger).toHaveBeenCalledTimes(1);
  174. expect(GroupStore.trigger).toHaveBeenCalledWith(new Set(['1', '2', '3']));
  175. });
  176. });
  177. describe('onDeleteSuccess()', function () {
  178. it("should treat undefined itemIds argument as 'all'", function () {
  179. GroupStore.onDeleteSuccess('1337', undefined, {});
  180. expect(GroupStore.trigger).toHaveBeenCalledTimes(1);
  181. expect(GroupStore.trigger).toHaveBeenCalledWith(new Set(['1', '2', '3']));
  182. });
  183. });
  184. describe('onAssignToSuccess()', function () {
  185. it("should treat undefined itemIds argument as 'all'", function () {
  186. GroupStore.items = [g('1')];
  187. const assignedGroup = g('1', {assignedTo: ActorFixture()});
  188. GroupStore.onAssignToSuccess('1337', '1', assignedGroup);
  189. expect(GroupStore.trigger).toHaveBeenCalledTimes(1);
  190. expect(GroupStore.trigger).toHaveBeenCalledWith(new Set(['1']));
  191. expect(GroupStore.items[0]).toEqual(assignedGroup);
  192. });
  193. });
  194. describe('updateActivity()', function () {
  195. it("should update activity data text'", function () {
  196. GroupStore.items = [
  197. g('1', {
  198. activity: [
  199. {
  200. id: '1',
  201. type: GroupActivityType.NOTE,
  202. dateCreated: '',
  203. project: ProjectFixture(),
  204. data: {text: 'Orginal Text'},
  205. },
  206. ],
  207. }),
  208. ];
  209. GroupStore.updateActivity('1', '1', {text: 'Updated Text'});
  210. expect(GroupStore.items[0].activity[0].data).toEqual({text: 'Updated Text'});
  211. });
  212. });
  213. });
  214. });