logExperiment.spec.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {unassignedValue} from 'sentry/data/experimentConfig';
  3. import ConfigStore from 'sentry/stores/configStore';
  4. import localStorage from 'sentry/utils/localStorage';
  5. import logExperiment from 'getsentry/utils/logExperiment';
  6. jest.mock('sentry/utils/localStorage');
  7. jest.mock('sentry/data/experimentConfig', () => ({
  8. experimentConfig: {
  9. orgExperiment: {
  10. key: 'orgExperiment',
  11. type: 'organization',
  12. parameter: 'exposed',
  13. assignments: [1, 0, -1],
  14. },
  15. variantExperiment: {
  16. key: 'variantExperiment',
  17. type: 'organization',
  18. parameter: 'variant',
  19. assignments: [1, 0, -1],
  20. },
  21. userExperiment: {
  22. key: 'userExperiment',
  23. type: 'user',
  24. parameter: 'exposed',
  25. assignments: [1, 0, -1],
  26. },
  27. },
  28. }));
  29. const mockedLocalStorageGetItem = localStorage.getItem as jest.MockedFunction<
  30. typeof localStorage.getItem
  31. >;
  32. describe('logExperiment', function () {
  33. afterEach(function () {
  34. jest.clearAllMocks();
  35. mockedLocalStorageGetItem.mockClear();
  36. });
  37. it('logs organization experiments', async function () {
  38. const organization = OrganizationFixture({
  39. id: '7',
  40. experiments: {
  41. // @ts-expect-error: Using mock keys
  42. orgExperiment: 1,
  43. },
  44. });
  45. const data = {
  46. experiment_name: 'orgExperiment',
  47. unit_name: 'org_id',
  48. unit_id: parseInt(organization.id, 10),
  49. params: {exposed: 1},
  50. };
  51. const mock = MockApiClient.addMockResponse({
  52. url: '/_experiment/log_exposure/',
  53. method: 'POST',
  54. body: data,
  55. });
  56. logExperiment({
  57. organization,
  58. // @ts-expect-error: Using mock keys
  59. key: 'orgExperiment',
  60. });
  61. await tick();
  62. expect(mock).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({data}));
  63. expect(localStorage.setItem).toHaveBeenCalledWith(
  64. 'logged-sentry-experiments',
  65. JSON.stringify({orgExperiment: parseInt(organization.id, 10)})
  66. );
  67. });
  68. it('logs user experiments', function () {
  69. ConfigStore.set('user', {
  70. ...ConfigStore.get('user'),
  71. id: '123',
  72. isSuperuser: false,
  73. experiments: {
  74. userExperiment: 1,
  75. },
  76. });
  77. const data = {
  78. experiment_name: 'userExperiment',
  79. unit_name: 'user_id',
  80. unit_id: 123,
  81. params: {exposed: 1},
  82. };
  83. const mock = MockApiClient.addMockResponse({
  84. url: '/_experiment/log_exposure/',
  85. method: 'POST',
  86. body: data,
  87. });
  88. logExperiment({
  89. // @ts-expect-error: Using mock keys
  90. key: 'userExperiment',
  91. });
  92. expect(mock).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({data}));
  93. });
  94. it('logs different parameters', function () {
  95. const organization = OrganizationFixture({
  96. id: '1',
  97. experiments: {
  98. // @ts-expect-error: Using mock keys
  99. variantExperiment: 1,
  100. },
  101. });
  102. const data = {
  103. experiment_name: 'variantExperiment',
  104. unit_name: 'org_id',
  105. unit_id: parseInt(organization.id, 10),
  106. params: {variant: 1},
  107. };
  108. const mock = MockApiClient.addMockResponse({
  109. url: '/_experiment/log_exposure/',
  110. method: 'POST',
  111. body: data,
  112. });
  113. logExperiment({
  114. organization,
  115. // @ts-expect-error: Using mock keys
  116. key: 'variantExperiment',
  117. });
  118. expect(mock).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({data}));
  119. });
  120. it('does not log unassigned experiments', function () {
  121. const mock = MockApiClient.addMockResponse({
  122. url: '/_experiment/log_exposure/',
  123. method: 'POST',
  124. });
  125. const organization = OrganizationFixture({
  126. id: '1',
  127. experiments: {
  128. // @ts-expect-error: Using mock keys
  129. orgExperiment: unassignedValue,
  130. },
  131. });
  132. logExperiment({
  133. organization,
  134. // @ts-expect-error: Using mock keys
  135. key: 'orgExperiment',
  136. });
  137. expect(mock).not.toHaveBeenCalled();
  138. });
  139. it('does not log when missing org', function () {
  140. const mock = MockApiClient.addMockResponse({
  141. url: '/_experiment/log_exposure/',
  142. method: 'POST',
  143. });
  144. logExperiment({
  145. organization: undefined,
  146. // @ts-expect-error: Using mock keys
  147. key: 'orgExperiment',
  148. });
  149. expect(mock).not.toHaveBeenCalled();
  150. });
  151. it("if experiment stored in local storage, don't call log_exposure", async function () {
  152. const organization = OrganizationFixture({
  153. id: '7',
  154. experiments: {
  155. // @ts-expect-error: Using mock keys
  156. orgExperiment: 1,
  157. },
  158. });
  159. mockedLocalStorageGetItem.mockImplementation(() =>
  160. JSON.stringify({orgExperiment: parseInt(organization.id, 10)})
  161. );
  162. const data = {
  163. experiment_name: 'orgExperiment',
  164. unit_name: 'org_id',
  165. unit_id: parseInt(organization.id, 10),
  166. params: {exposed: 1},
  167. };
  168. const mock = MockApiClient.addMockResponse({
  169. url: '/_experiment/log_exposure/',
  170. method: 'POST',
  171. body: data,
  172. });
  173. logExperiment({
  174. organization,
  175. // @ts-expect-error: Using mock keys
  176. key: 'orgExperiment',
  177. });
  178. await tick();
  179. expect(mock).not.toHaveBeenCalled();
  180. expect(localStorage.setItem).not.toHaveBeenCalled();
  181. });
  182. it('if local storage has different experiment, call log_exposure', async function () {
  183. const organization = OrganizationFixture({
  184. id: '7',
  185. experiments: {
  186. // @ts-expect-error: Using mock keys
  187. orgExperiment: 1,
  188. },
  189. });
  190. const unit_id = parseInt(organization.id, 10);
  191. mockedLocalStorageGetItem.mockImplementation(() =>
  192. JSON.stringify({anotherExperiment: unit_id})
  193. );
  194. const data = {
  195. experiment_name: 'orgExperiment',
  196. unit_name: 'org_id',
  197. unit_id,
  198. params: {exposed: 1},
  199. };
  200. const mock = MockApiClient.addMockResponse({
  201. url: '/_experiment/log_exposure/',
  202. method: 'POST',
  203. body: data,
  204. });
  205. logExperiment({
  206. organization,
  207. // @ts-expect-error: Using mock keys
  208. key: 'orgExperiment',
  209. });
  210. await tick();
  211. expect(mock).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({data}));
  212. expect(localStorage.setItem).toHaveBeenCalledWith(
  213. 'logged-sentry-experiments',
  214. JSON.stringify({anotherExperiment: unit_id, orgExperiment: unit_id})
  215. );
  216. });
  217. it('if local storage has different unit_id, call log_exposure', async function () {
  218. const organization = OrganizationFixture({
  219. id: '7',
  220. experiments: {
  221. // @ts-expect-error: Using mock keys
  222. orgExperiment: 1,
  223. },
  224. });
  225. const unit_id = parseInt(organization.id, 10);
  226. mockedLocalStorageGetItem.mockImplementation(() =>
  227. JSON.stringify({orgExperiment: 12})
  228. );
  229. const data = {
  230. experiment_name: 'orgExperiment',
  231. unit_name: 'org_id',
  232. unit_id,
  233. params: {exposed: 1},
  234. };
  235. const mock = MockApiClient.addMockResponse({
  236. url: '/_experiment/log_exposure/',
  237. method: 'POST',
  238. body: data,
  239. });
  240. logExperiment({
  241. organization,
  242. // @ts-expect-error: Using mock keys
  243. key: 'orgExperiment',
  244. });
  245. await tick();
  246. expect(mock).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({data}));
  247. expect(localStorage.setItem).toHaveBeenCalledWith(
  248. 'logged-sentry-experiments',
  249. JSON.stringify({orgExperiment: unit_id})
  250. );
  251. });
  252. });