RecurlyEventHandlerTests.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. const SandboxedModule = require('sandboxed-module')
  2. const sinon = require('sinon')
  3. const modulePath =
  4. '../../../../app/src/Features/Subscription/RecurlyEventHandler'
  5. describe('RecurlyEventHandler', function () {
  6. beforeEach(function () {
  7. this.userId = '123abc234bcd456cde567def'
  8. this.planCode = 'collaborator-annual'
  9. this.eventData = {
  10. account: {
  11. account_code: this.userId,
  12. },
  13. subscription: {
  14. uuid: '8435ad98c1ce45da99b07f6a6a2e780f',
  15. plan: {
  16. plan_code: 'collaborator-annual',
  17. },
  18. quantity: 1,
  19. state: 'active',
  20. trial_started_at: new Date('2021-01-01 12:34:56'),
  21. trial_ends_at: new Date('2021-01-08 12:34:56'),
  22. current_period_started_at: new Date('2021-01-01 12:34:56'),
  23. current_period_ends_at: new Date('2021-01-08 12:34:56'),
  24. },
  25. }
  26. this.RecurlyEventHandler = SandboxedModule.require(modulePath, {
  27. requires: {
  28. './SubscriptionEmailHandler': (this.SubscriptionEmailHandler = {
  29. sendTrialOnboardingEmail: sinon.stub(),
  30. }),
  31. '../Analytics/AnalyticsManager': (this.AnalyticsManager = {
  32. recordEventForUser: sinon.stub(),
  33. setUserPropertyForUser: sinon.stub(),
  34. }),
  35. '../SplitTests/SplitTestHandler': (this.SplitTestHandler = {
  36. promises: {
  37. getAssignmentForUser: sinon.stub().resolves({
  38. variant: 'default',
  39. }),
  40. },
  41. }),
  42. },
  43. })
  44. })
  45. it('with new_subscription_notification - free trial', function () {
  46. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  47. 'new_subscription_notification',
  48. this.eventData
  49. )
  50. sinon.assert.calledWith(
  51. this.AnalyticsManager.recordEventForUser,
  52. this.userId,
  53. 'subscription-started',
  54. {
  55. plan_code: this.planCode,
  56. quantity: 1,
  57. is_trial: true,
  58. subscriptionId: this.eventData.subscription.uuid,
  59. }
  60. )
  61. sinon.assert.calledWith(
  62. this.AnalyticsManager.setUserPropertyForUser,
  63. this.userId,
  64. 'subscription-plan-code',
  65. this.planCode
  66. )
  67. sinon.assert.calledWith(
  68. this.AnalyticsManager.setUserPropertyForUser,
  69. this.userId,
  70. 'subscription-state',
  71. 'active'
  72. )
  73. sinon.assert.calledWith(
  74. this.AnalyticsManager.setUserPropertyForUser,
  75. this.userId,
  76. 'subscription-is-trial',
  77. true
  78. )
  79. })
  80. it('sends free trial onboarding email if user starting a trial', async function () {
  81. await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  82. 'new_subscription_notification',
  83. this.eventData
  84. )
  85. sinon.assert.called(this.SubscriptionEmailHandler.sendTrialOnboardingEmail)
  86. })
  87. it('with new_subscription_notification - no free trial', function () {
  88. this.eventData.subscription.current_period_started_at = new Date(
  89. '2021-02-10 12:34:56'
  90. )
  91. this.eventData.subscription.current_period_ends_at = new Date(
  92. '2021-02-17 12:34:56'
  93. )
  94. this.eventData.subscription.quantity = 3
  95. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  96. 'new_subscription_notification',
  97. this.eventData
  98. )
  99. sinon.assert.calledWith(
  100. this.AnalyticsManager.recordEventForUser,
  101. this.userId,
  102. 'subscription-started',
  103. {
  104. plan_code: this.planCode,
  105. quantity: 3,
  106. is_trial: false,
  107. subscriptionId: this.eventData.subscription.uuid,
  108. }
  109. )
  110. sinon.assert.calledWith(
  111. this.AnalyticsManager.setUserPropertyForUser,
  112. this.userId,
  113. 'subscription-state',
  114. 'active'
  115. )
  116. sinon.assert.calledWith(
  117. this.AnalyticsManager.setUserPropertyForUser,
  118. this.userId,
  119. 'subscription-is-trial',
  120. false
  121. )
  122. })
  123. it('with updated_subscription_notification', function () {
  124. this.planCode = 'new-plan-code'
  125. this.eventData.subscription.plan.plan_code = this.planCode
  126. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  127. 'updated_subscription_notification',
  128. this.eventData
  129. )
  130. sinon.assert.calledWith(
  131. this.AnalyticsManager.recordEventForUser,
  132. this.userId,
  133. 'subscription-updated',
  134. {
  135. plan_code: this.planCode,
  136. quantity: 1,
  137. is_trial: true,
  138. subscriptionId: this.eventData.subscription.uuid,
  139. }
  140. )
  141. sinon.assert.calledWith(
  142. this.AnalyticsManager.setUserPropertyForUser,
  143. this.userId,
  144. 'subscription-plan-code',
  145. this.planCode
  146. )
  147. sinon.assert.calledWith(
  148. this.AnalyticsManager.setUserPropertyForUser,
  149. this.userId,
  150. 'subscription-state',
  151. 'active'
  152. )
  153. sinon.assert.calledWith(
  154. this.AnalyticsManager.setUserPropertyForUser,
  155. this.userId,
  156. 'subscription-is-trial',
  157. true
  158. )
  159. })
  160. it('with canceled_subscription_notification', async function () {
  161. this.eventData.subscription.state = 'cancelled'
  162. await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  163. 'canceled_subscription_notification',
  164. this.eventData
  165. )
  166. sinon.assert.calledWith(
  167. this.SplitTestHandler.promises.getAssignmentForUser,
  168. this.userId,
  169. 'design-system-updates'
  170. )
  171. sinon.assert.calledWith(
  172. this.AnalyticsManager.recordEventForUser,
  173. this.userId,
  174. 'subscription-cancelled',
  175. {
  176. plan_code: this.planCode,
  177. quantity: 1,
  178. is_trial: true,
  179. subscriptionId: this.eventData.subscription.uuid,
  180. 'split-test-design-system-updates': 'default',
  181. }
  182. )
  183. sinon.assert.calledWith(
  184. this.AnalyticsManager.setUserPropertyForUser,
  185. this.userId,
  186. 'subscription-state',
  187. 'cancelled'
  188. )
  189. sinon.assert.calledWith(
  190. this.AnalyticsManager.setUserPropertyForUser,
  191. this.userId,
  192. 'subscription-is-trial',
  193. true
  194. )
  195. })
  196. it('with expired_subscription_notification', function () {
  197. this.eventData.subscription.state = 'expired'
  198. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  199. 'expired_subscription_notification',
  200. this.eventData
  201. )
  202. sinon.assert.calledWith(
  203. this.AnalyticsManager.recordEventForUser,
  204. this.userId,
  205. 'subscription-expired',
  206. {
  207. plan_code: this.planCode,
  208. quantity: 1,
  209. is_trial: true,
  210. subscriptionId: this.eventData.subscription.uuid,
  211. }
  212. )
  213. sinon.assert.calledWith(
  214. this.AnalyticsManager.setUserPropertyForUser,
  215. this.userId,
  216. 'subscription-plan-code',
  217. this.planCode
  218. )
  219. sinon.assert.calledWith(
  220. this.AnalyticsManager.setUserPropertyForUser,
  221. this.userId,
  222. 'subscription-state',
  223. 'expired'
  224. )
  225. sinon.assert.calledWith(
  226. this.AnalyticsManager.setUserPropertyForUser,
  227. this.userId,
  228. 'subscription-is-trial',
  229. true
  230. )
  231. })
  232. it('with renewed_subscription_notification', function () {
  233. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  234. 'renewed_subscription_notification',
  235. this.eventData
  236. )
  237. sinon.assert.calledWith(
  238. this.AnalyticsManager.recordEventForUser,
  239. this.userId,
  240. 'subscription-renewed',
  241. {
  242. plan_code: this.planCode,
  243. quantity: 1,
  244. is_trial: true,
  245. subscriptionId: this.eventData.subscription.uuid,
  246. }
  247. )
  248. })
  249. it('with reactivated_account_notification', function () {
  250. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  251. 'reactivated_account_notification',
  252. this.eventData
  253. )
  254. sinon.assert.calledWith(
  255. this.AnalyticsManager.recordEventForUser,
  256. this.userId,
  257. 'subscription-reactivated',
  258. {
  259. plan_code: this.planCode,
  260. quantity: 1,
  261. subscriptionId: this.eventData.subscription.uuid,
  262. }
  263. )
  264. })
  265. it('with paid_charge_invoice_notification', function () {
  266. const invoice = {
  267. invoice_number: 1234,
  268. currency: 'USD',
  269. state: 'paid',
  270. total_in_cents: 720,
  271. tax_in_cents: 12,
  272. address: {
  273. country: 'Liurnia',
  274. },
  275. collection_method: 'automatic',
  276. subscription_ids: ['abcd1234', 'defa3214'],
  277. }
  278. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  279. 'paid_charge_invoice_notification',
  280. {
  281. account: {
  282. account_code: this.userId,
  283. },
  284. invoice,
  285. }
  286. )
  287. sinon.assert.calledWith(
  288. this.AnalyticsManager.recordEventForUser,
  289. this.userId,
  290. 'subscription-invoice-collected',
  291. {
  292. invoiceNumber: invoice.invoice_number,
  293. currency: invoice.currency,
  294. totalInCents: invoice.total_in_cents,
  295. taxInCents: invoice.tax_in_cents,
  296. country: invoice.address.country,
  297. collectionMethod: invoice.collection_method,
  298. subscriptionId1: invoice.subscription_ids[0],
  299. subscriptionId2: invoice.subscription_ids[1],
  300. }
  301. )
  302. })
  303. it('with paid_charge_invoice_notification and total_in_cents 0', function () {
  304. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  305. 'paid_charge_invoice_notification',
  306. {
  307. account: {
  308. account_code: this.userId,
  309. },
  310. invoice: {
  311. state: 'paid',
  312. total_in_cents: 0,
  313. },
  314. }
  315. )
  316. sinon.assert.notCalled(this.AnalyticsManager.recordEventForUser)
  317. })
  318. it('with closed_invoice_notification', function () {
  319. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  320. 'closed_invoice_notification',
  321. {
  322. account: {
  323. account_code: this.userId,
  324. },
  325. invoice: {
  326. state: 'collected',
  327. total_in_cents: 720,
  328. },
  329. }
  330. )
  331. sinon.assert.calledWith(
  332. this.AnalyticsManager.recordEventForUser,
  333. this.userId,
  334. 'subscription-invoice-collected'
  335. )
  336. })
  337. it('with closed_invoice_notification and total_in_cents 0', function () {
  338. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  339. 'closed_invoice_notification',
  340. {
  341. account: {
  342. account_code: this.userId,
  343. },
  344. invoice: {
  345. state: 'collected',
  346. total_in_cents: 0,
  347. },
  348. }
  349. )
  350. sinon.assert.notCalled(this.AnalyticsManager.recordEventForUser)
  351. })
  352. it('nothing is called with invalid account code', function () {
  353. this.eventData.account.account_code = 'foo_bar'
  354. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  355. 'new_subscription_notification',
  356. this.eventData
  357. )
  358. sinon.assert.notCalled(this.AnalyticsManager.recordEventForUser)
  359. sinon.assert.notCalled(this.AnalyticsManager.setUserPropertyForUser)
  360. sinon.assert.notCalled(this.AnalyticsManager.setUserPropertyForUser)
  361. sinon.assert.notCalled(this.AnalyticsManager.setUserPropertyForUser)
  362. })
  363. })