RecurlyEventHandlerTests.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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.AnalyticsManager.recordEventForUser,
  168. this.userId,
  169. 'subscription-cancelled',
  170. {
  171. plan_code: this.planCode,
  172. quantity: 1,
  173. is_trial: true,
  174. subscriptionId: this.eventData.subscription.uuid,
  175. }
  176. )
  177. sinon.assert.calledWith(
  178. this.AnalyticsManager.setUserPropertyForUser,
  179. this.userId,
  180. 'subscription-state',
  181. 'cancelled'
  182. )
  183. sinon.assert.calledWith(
  184. this.AnalyticsManager.setUserPropertyForUser,
  185. this.userId,
  186. 'subscription-is-trial',
  187. true
  188. )
  189. })
  190. it('with expired_subscription_notification', function () {
  191. this.eventData.subscription.state = 'expired'
  192. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  193. 'expired_subscription_notification',
  194. this.eventData
  195. )
  196. sinon.assert.calledWith(
  197. this.AnalyticsManager.recordEventForUser,
  198. this.userId,
  199. 'subscription-expired',
  200. {
  201. plan_code: this.planCode,
  202. quantity: 1,
  203. is_trial: true,
  204. subscriptionId: this.eventData.subscription.uuid,
  205. }
  206. )
  207. sinon.assert.calledWith(
  208. this.AnalyticsManager.setUserPropertyForUser,
  209. this.userId,
  210. 'subscription-plan-code',
  211. this.planCode
  212. )
  213. sinon.assert.calledWith(
  214. this.AnalyticsManager.setUserPropertyForUser,
  215. this.userId,
  216. 'subscription-state',
  217. 'expired'
  218. )
  219. sinon.assert.calledWith(
  220. this.AnalyticsManager.setUserPropertyForUser,
  221. this.userId,
  222. 'subscription-is-trial',
  223. true
  224. )
  225. })
  226. it('with renewed_subscription_notification', function () {
  227. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  228. 'renewed_subscription_notification',
  229. this.eventData
  230. )
  231. sinon.assert.calledWith(
  232. this.AnalyticsManager.recordEventForUser,
  233. this.userId,
  234. 'subscription-renewed',
  235. {
  236. plan_code: this.planCode,
  237. quantity: 1,
  238. is_trial: true,
  239. subscriptionId: this.eventData.subscription.uuid,
  240. }
  241. )
  242. })
  243. it('with reactivated_account_notification', function () {
  244. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  245. 'reactivated_account_notification',
  246. this.eventData
  247. )
  248. sinon.assert.calledWith(
  249. this.AnalyticsManager.recordEventForUser,
  250. this.userId,
  251. 'subscription-reactivated',
  252. {
  253. plan_code: this.planCode,
  254. quantity: 1,
  255. subscriptionId: this.eventData.subscription.uuid,
  256. }
  257. )
  258. })
  259. it('with paid_charge_invoice_notification', function () {
  260. const invoice = {
  261. invoice_number: 1234,
  262. currency: 'USD',
  263. state: 'paid',
  264. total_in_cents: 720,
  265. tax_in_cents: 12,
  266. address: {
  267. country: 'Liurnia',
  268. },
  269. collection_method: 'automatic',
  270. subscription_ids: ['abcd1234', 'defa3214'],
  271. }
  272. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  273. 'paid_charge_invoice_notification',
  274. {
  275. account: {
  276. account_code: this.userId,
  277. },
  278. invoice,
  279. }
  280. )
  281. sinon.assert.calledWith(
  282. this.AnalyticsManager.recordEventForUser,
  283. this.userId,
  284. 'subscription-invoice-collected',
  285. {
  286. invoiceNumber: invoice.invoice_number,
  287. currency: invoice.currency,
  288. totalInCents: invoice.total_in_cents,
  289. taxInCents: invoice.tax_in_cents,
  290. country: invoice.address.country,
  291. collectionMethod: invoice.collection_method,
  292. subscriptionId1: invoice.subscription_ids[0],
  293. subscriptionId2: invoice.subscription_ids[1],
  294. }
  295. )
  296. })
  297. it('with paid_charge_invoice_notification and total_in_cents 0', function () {
  298. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  299. 'paid_charge_invoice_notification',
  300. {
  301. account: {
  302. account_code: this.userId,
  303. },
  304. invoice: {
  305. state: 'paid',
  306. total_in_cents: 0,
  307. },
  308. }
  309. )
  310. sinon.assert.notCalled(this.AnalyticsManager.recordEventForUser)
  311. })
  312. it('with closed_invoice_notification', function () {
  313. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  314. 'closed_invoice_notification',
  315. {
  316. account: {
  317. account_code: this.userId,
  318. },
  319. invoice: {
  320. state: 'collected',
  321. total_in_cents: 720,
  322. },
  323. }
  324. )
  325. sinon.assert.calledWith(
  326. this.AnalyticsManager.recordEventForUser,
  327. this.userId,
  328. 'subscription-invoice-collected'
  329. )
  330. })
  331. it('with closed_invoice_notification and total_in_cents 0', function () {
  332. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  333. 'closed_invoice_notification',
  334. {
  335. account: {
  336. account_code: this.userId,
  337. },
  338. invoice: {
  339. state: 'collected',
  340. total_in_cents: 0,
  341. },
  342. }
  343. )
  344. sinon.assert.notCalled(this.AnalyticsManager.recordEventForUser)
  345. })
  346. it('nothing is called with invalid account code', function () {
  347. this.eventData.account.account_code = 'foo_bar'
  348. this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
  349. 'new_subscription_notification',
  350. this.eventData
  351. )
  352. sinon.assert.notCalled(this.AnalyticsManager.recordEventForUser)
  353. sinon.assert.notCalled(this.AnalyticsManager.setUserPropertyForUser)
  354. sinon.assert.notCalled(this.AnalyticsManager.setUserPropertyForUser)
  355. sinon.assert.notCalled(this.AnalyticsManager.setUserPropertyForUser)
  356. })
  357. })