PlansLocatorTests.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. const SandboxedModule = require('sandboxed-module')
  2. const { expect } = require('chai')
  3. const modulePath = '../../../../app/src/Features/Subscription/PlansLocator'
  4. const plans = [
  5. {
  6. planCode: 'first',
  7. name: '1st',
  8. price_in_cents: 800,
  9. features: {},
  10. featureDescription: {},
  11. },
  12. {
  13. planCode: 'second',
  14. name: '2nd',
  15. price_in_cents: 1500,
  16. features: {},
  17. featureDescription: {},
  18. },
  19. {
  20. planCode: 'third',
  21. name: '3rd',
  22. price_in_cents: 3000,
  23. features: {},
  24. featureDescription: {},
  25. },
  26. ]
  27. describe('PlansLocator', function () {
  28. beforeEach(function () {
  29. this.settings = { plans }
  30. this.AI_ADD_ON_CODE = 'assistant'
  31. this.PlansLocator = SandboxedModule.require(modulePath, {
  32. requires: {
  33. '@overleaf/settings': this.settings,
  34. },
  35. })
  36. })
  37. describe('findLocalPlanInSettings', function () {
  38. it('should return the found plan', function () {
  39. const plan = this.PlansLocator.findLocalPlanInSettings('second')
  40. expect(plan).to.have.property('name', '2nd')
  41. expect(plan).to.have.property('price_in_cents', 1500)
  42. })
  43. it('should return null if no matching plan is found', function () {
  44. const plan = this.PlansLocator.findLocalPlanInSettings('gibberish')
  45. expect(plan).to.be.a('null')
  46. })
  47. })
  48. describe('buildStripeLookupKey', function () {
  49. it('should map "collaborator" plan code to stripe lookup keys', function () {
  50. const planCode = 'collaborator'
  51. const currency = 'eur'
  52. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  53. planCode,
  54. currency
  55. )
  56. expect(lookupKey).to.equal('standard_monthly_jun2025_eur')
  57. })
  58. it('should map "collaborator_free_trial_7_days" plan code to stripe lookup keys', function () {
  59. const planCode = 'collaborator_free_trial_7_days'
  60. const currency = 'eur'
  61. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  62. planCode,
  63. currency
  64. )
  65. expect(lookupKey).to.equal('standard_monthly_jun2025_eur')
  66. })
  67. it('should map "collaborator-annual" plan code to stripe lookup keys', function () {
  68. const planCode = 'collaborator-annual'
  69. const currency = 'eur'
  70. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  71. planCode,
  72. currency
  73. )
  74. expect(lookupKey).to.equal('standard_annual_jun2025_eur')
  75. })
  76. it('should map "professional" plan code to stripe lookup keys', function () {
  77. const planCode = 'professional'
  78. const currency = 'eur'
  79. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  80. planCode,
  81. currency
  82. )
  83. expect(lookupKey).to.equal('professional_monthly_jun2025_eur')
  84. })
  85. it('should map "professional_free_trial_7_days" plan code to stripe lookup keys', function () {
  86. const planCode = 'professional_free_trial_7_days'
  87. const currency = 'eur'
  88. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  89. planCode,
  90. currency
  91. )
  92. expect(lookupKey).to.equal('professional_monthly_jun2025_eur')
  93. })
  94. it('should map "professional-annual" plan code to stripe lookup keys', function () {
  95. const planCode = 'professional-annual'
  96. const currency = 'eur'
  97. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  98. planCode,
  99. currency
  100. )
  101. expect(lookupKey).to.equal('professional_annual_jun2025_eur')
  102. })
  103. it('should map "student" plan code to stripe lookup keys', function () {
  104. const planCode = 'student'
  105. const currency = 'eur'
  106. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  107. planCode,
  108. currency
  109. )
  110. expect(lookupKey).to.equal('student_monthly_jun2025_eur')
  111. })
  112. it('shoult map "student_free_trial_7_days" plan code to stripe lookup keys', function () {
  113. const planCode = 'student_free_trial_7_days'
  114. const currency = 'eur'
  115. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  116. planCode,
  117. currency
  118. )
  119. expect(lookupKey).to.equal('student_monthly_jun2025_eur')
  120. })
  121. it('should map "student-annual" plan code to stripe lookup keys', function () {
  122. const planCode = 'student-annual'
  123. const currency = 'eur'
  124. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  125. planCode,
  126. currency
  127. )
  128. expect(lookupKey).to.equal('student_annual_jun2025_eur')
  129. })
  130. it('should return null for unknown add-on codes', function () {
  131. const billingCycleInterval = 'month'
  132. const addOnCode = 'unknown_addon'
  133. const currency = 'gbp'
  134. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  135. addOnCode,
  136. currency,
  137. billingCycleInterval
  138. )
  139. expect(lookupKey).to.equal(null)
  140. })
  141. it('should handle missing input', function () {
  142. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  143. undefined,
  144. undefined
  145. )
  146. expect(lookupKey).to.equal(null)
  147. })
  148. it('returns the key for a monthly AI assist add-on', function () {
  149. const billingCycleInterval = 'month'
  150. const addOnCode = this.AI_ADD_ON_CODE
  151. const currency = 'gbp'
  152. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  153. addOnCode,
  154. currency,
  155. billingCycleInterval
  156. )
  157. expect(lookupKey).to.equal('assistant_monthly_jun2025_gbp')
  158. })
  159. it('returns the key for an annual AI assist add-on', function () {
  160. const billingCycleInterval = 'year'
  161. const addOnCode = this.AI_ADD_ON_CODE
  162. const currency = 'gbp'
  163. const lookupKey = this.PlansLocator.buildStripeLookupKey(
  164. addOnCode,
  165. currency,
  166. billingCycleInterval
  167. )
  168. expect(lookupKey).to.equal('assistant_annual_jun2025_gbp')
  169. })
  170. })
  171. describe('getPlanTypeAndPeriodFromRecurlyPlanCode', function () {
  172. it('should return the plan type and period for "collaborator"', function () {
  173. const { planType, period } =
  174. this.PlansLocator.getPlanTypeAndPeriodFromRecurlyPlanCode(
  175. 'collaborator'
  176. )
  177. expect(planType).to.equal('individual')
  178. expect(period).to.equal('monthly')
  179. })
  180. it('should return the plan type and period for "collaborator_free_trial_7_days"', function () {
  181. const { planType, period } =
  182. this.PlansLocator.getPlanTypeAndPeriodFromRecurlyPlanCode(
  183. 'collaborator_free_trial_7_days'
  184. )
  185. expect(planType).to.equal('individual')
  186. expect(period).to.equal('monthly')
  187. })
  188. it('should return the plan type and period for "collaborator-annual"', function () {
  189. const { planType, period } =
  190. this.PlansLocator.getPlanTypeAndPeriodFromRecurlyPlanCode(
  191. 'collaborator-annual'
  192. )
  193. expect(planType).to.equal('individual')
  194. expect(period).to.equal('annual')
  195. })
  196. it('should return the plan type and period for "professional"', function () {
  197. const { planType, period } =
  198. this.PlansLocator.getPlanTypeAndPeriodFromRecurlyPlanCode(
  199. 'professional'
  200. )
  201. expect(planType).to.equal('individual')
  202. expect(period).to.equal('monthly')
  203. })
  204. it('should return the plan type and period for "professional_free_trial_7_days"', function () {
  205. const { planType, period } =
  206. this.PlansLocator.getPlanTypeAndPeriodFromRecurlyPlanCode(
  207. 'professional_free_trial_7_days'
  208. )
  209. expect(planType).to.equal('individual')
  210. expect(period).to.equal('monthly')
  211. })
  212. it('should return the plan type and period for "professional-annual"', function () {
  213. const { planType, period } =
  214. this.PlansLocator.getPlanTypeAndPeriodFromRecurlyPlanCode(
  215. 'professional-annual'
  216. )
  217. expect(planType).to.equal('individual')
  218. expect(period).to.equal('annual')
  219. })
  220. it('should return the plan type and period for "student"', function () {
  221. const { planType, period } =
  222. this.PlansLocator.getPlanTypeAndPeriodFromRecurlyPlanCode('student')
  223. expect(planType).to.equal('student')
  224. expect(period).to.equal('monthly')
  225. })
  226. it('should return the plan type and period for "student_free_trial_7_days"', function () {
  227. const { planType, period } =
  228. this.PlansLocator.getPlanTypeAndPeriodFromRecurlyPlanCode(
  229. 'student_free_trial_7_days'
  230. )
  231. expect(planType).to.equal('student')
  232. expect(period).to.equal('monthly')
  233. })
  234. it('should return the plan type and period for "student-annual"', function () {
  235. const { planType, period } =
  236. this.PlansLocator.getPlanTypeAndPeriodFromRecurlyPlanCode(
  237. 'student-annual'
  238. )
  239. expect(planType).to.equal('student')
  240. expect(period).to.equal('annual')
  241. })
  242. })
  243. describe('convertLegacyGroupPlanCodeToConsolidatedGroupPlanCodeIfNeeded', function () {
  244. it('returns original plan name for non-group plan codes', function () {
  245. expect(
  246. this.PlansLocator.convertLegacyGroupPlanCodeToConsolidatedGroupPlanCodeIfNeeded(
  247. 'professional'
  248. )
  249. ).to.deep.equal({
  250. planCode: 'professional',
  251. quantity: 1,
  252. })
  253. })
  254. it('converts Recurly enterprise group plan codes to Stripe group plan codes', function () {
  255. expect(
  256. this.PlansLocator.convertLegacyGroupPlanCodeToConsolidatedGroupPlanCodeIfNeeded(
  257. 'group_collaborator_10_enterprise'
  258. )
  259. ).to.deep.equal({
  260. planCode: 'group_collaborator',
  261. quantity: 10,
  262. })
  263. })
  264. it('converts Recurly educational group plan codes to Stripe group plan codes', function () {
  265. expect(
  266. this.PlansLocator.convertLegacyGroupPlanCodeToConsolidatedGroupPlanCodeIfNeeded(
  267. 'group_professional_10_educational'
  268. )
  269. ).to.deep.equal({
  270. planCode: 'group_professional_educational',
  271. quantity: 10,
  272. })
  273. })
  274. })
  275. })