SubscriptionHelperTests.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. const SandboxedModule = require('sandboxed-module')
  2. const { expect } = require('chai')
  3. const modulePath =
  4. '../../../../app/src/Features/Subscription/SubscriptionHelper'
  5. const plans = {
  6. expensive: {
  7. planCode: 'expensive',
  8. price_in_cents: 1500,
  9. },
  10. cheaper: {
  11. planCode: 'cheaper',
  12. price_in_cents: 500,
  13. },
  14. alsoCheap: {
  15. plancode: 'also-cheap',
  16. price_in_cents: 500,
  17. },
  18. expensiveGroup: {
  19. plancode: 'group_expensive',
  20. price_in_cents: 49500,
  21. groupPlan: true,
  22. },
  23. cheapGroup: {
  24. plancode: 'group_cheap',
  25. price_in_cents: 1000,
  26. groupPlan: true,
  27. },
  28. bad: {},
  29. }
  30. describe('SubscriptionHelper', function () {
  31. beforeEach(function () {
  32. this.INITIAL_LICENSE_SIZE = 2
  33. this.mockCollaboratorPrice = 2000
  34. this.mockProfessionalPrice = 4000
  35. this.settings = {
  36. groupPlanModalOptions: {
  37. currencySymbols: {
  38. USD: '$',
  39. CHF: 'Fr',
  40. DKK: 'kr',
  41. NOK: 'kr',
  42. SEK: 'kr',
  43. },
  44. },
  45. }
  46. this.GroupPlansData = {
  47. enterprise: {
  48. collaborator: {
  49. USD: {
  50. [this.INITIAL_LICENSE_SIZE]: {
  51. price_in_cents: this.mockCollaboratorPrice,
  52. },
  53. },
  54. CHF: {
  55. [this.INITIAL_LICENSE_SIZE]: {
  56. price_in_cents: this.mockCollaboratorPrice,
  57. },
  58. },
  59. DKK: {
  60. [this.INITIAL_LICENSE_SIZE]: {
  61. price_in_cents: this.mockCollaboratorPrice,
  62. },
  63. },
  64. NOK: {
  65. [this.INITIAL_LICENSE_SIZE]: {
  66. price_in_cents: this.mockCollaboratorPrice,
  67. },
  68. },
  69. SEK: {
  70. [this.INITIAL_LICENSE_SIZE]: {
  71. price_in_cents: this.mockCollaboratorPrice,
  72. },
  73. },
  74. },
  75. professional: {
  76. USD: {
  77. [this.INITIAL_LICENSE_SIZE]: {
  78. price_in_cents: this.mockProfessionalPrice,
  79. },
  80. },
  81. CHF: {
  82. [this.INITIAL_LICENSE_SIZE]: {
  83. price_in_cents: this.mockProfessionalPrice,
  84. },
  85. },
  86. DKK: {
  87. [this.INITIAL_LICENSE_SIZE]: {
  88. price_in_cents: this.mockProfessionalPrice,
  89. },
  90. },
  91. NOK: {
  92. [this.INITIAL_LICENSE_SIZE]: {
  93. price_in_cents: this.mockProfessionalPrice,
  94. },
  95. },
  96. SEK: {
  97. [this.INITIAL_LICENSE_SIZE]: {
  98. price_in_cents: this.mockProfessionalPrice,
  99. },
  100. },
  101. },
  102. },
  103. }
  104. this.SubscriptionHelper = SandboxedModule.require(modulePath, {
  105. requires: {
  106. '@overleaf/settings': this.settings,
  107. './GroupPlansData': this.GroupPlansData,
  108. },
  109. })
  110. })
  111. describe('shouldPlanChangeAtTermEnd', function () {
  112. it('should return true if the new plan is less expensive', function () {
  113. const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
  114. plans.expensive,
  115. plans.cheaper
  116. )
  117. expect(changeAtTermEnd).to.be.true
  118. })
  119. it('should return false if the new plan is more exepensive', function () {
  120. const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
  121. plans.cheaper,
  122. plans.expensive
  123. )
  124. expect(changeAtTermEnd).to.be.false
  125. })
  126. it('should return false if the new plan is the same price', function () {
  127. const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
  128. plans.cheaper,
  129. plans.alsoCheap
  130. )
  131. expect(changeAtTermEnd).to.be.false
  132. })
  133. it('should return false if the change is from an individual plan to a more expensive group plan', function () {
  134. const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
  135. plans.expensive,
  136. plans.expensiveGroup
  137. )
  138. expect(changeAtTermEnd).to.be.false
  139. })
  140. it('should return true if the change is from an individual plan to a cheaper group plan', function () {
  141. const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
  142. plans.expensive,
  143. plans.cheapGroup
  144. )
  145. expect(changeAtTermEnd).to.be.true
  146. })
  147. })
  148. describe('generateInitialLocalizedGroupPrice', function () {
  149. describe('collaborator plan', function () {
  150. beforeEach(function () {
  151. this.plan = 'collaborator'
  152. })
  153. describe('for CHF currency', function () {
  154. beforeEach(function () {
  155. this.mockRecommendedCurrency = 'CHF'
  156. this.recommendedCurrencySymbol =
  157. this.settings.groupPlanModalOptions.currencySymbols[
  158. this.mockRecommendedCurrency
  159. ]
  160. this.expectedPrice =
  161. this.GroupPlansData.enterprise[this.plan][
  162. this.mockRecommendedCurrency
  163. ][this.INITIAL_LICENSE_SIZE].price_in_cents / 100
  164. })
  165. it('should return the correct localized price', function () {
  166. const expectedLocalizedPrice = `${this.recommendedCurrencySymbol} ${this.expectedPrice}`
  167. const {
  168. price: { collaborator },
  169. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  170. this.mockRecommendedCurrency
  171. )
  172. expect(collaborator).to.be.equal(expectedLocalizedPrice)
  173. })
  174. it('should return the correct localized price per user', function () {
  175. const expectedPricePerUser =
  176. this.expectedPrice / this.INITIAL_LICENSE_SIZE
  177. const expectedLocalizedPricePerUser = `${this.recommendedCurrencySymbol} ${expectedPricePerUser}`
  178. const {
  179. pricePerUser: { collaborator },
  180. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  181. this.mockRecommendedCurrency
  182. )
  183. expect(collaborator).to.be.equal(expectedLocalizedPricePerUser)
  184. })
  185. })
  186. describe('for DKK currency', function () {
  187. beforeEach(function () {
  188. this.mockRecommendedCurrency = 'DKK'
  189. this.recommendedCurrencySymbol =
  190. this.settings.groupPlanModalOptions.currencySymbols[
  191. this.mockRecommendedCurrency
  192. ]
  193. this.expectedPrice =
  194. this.GroupPlansData.enterprise[this.plan][
  195. this.mockRecommendedCurrency
  196. ][this.INITIAL_LICENSE_SIZE].price_in_cents / 100
  197. })
  198. it('should return the correct localized price', function () {
  199. const expectedLocalizedPrice = `${this.expectedPrice} ${this.recommendedCurrencySymbol}`
  200. const {
  201. price: { collaborator },
  202. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  203. this.mockRecommendedCurrency
  204. )
  205. expect(collaborator).to.be.equal(expectedLocalizedPrice)
  206. })
  207. it('should return the correct localized price per user', function () {
  208. const expectedPricePerUser =
  209. this.expectedPrice / this.INITIAL_LICENSE_SIZE
  210. const expectedLocalizedPricePerUser = `${expectedPricePerUser} ${this.recommendedCurrencySymbol}`
  211. const {
  212. pricePerUser: { collaborator },
  213. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  214. this.mockRecommendedCurrency
  215. )
  216. expect(collaborator).to.be.equal(expectedLocalizedPricePerUser)
  217. })
  218. })
  219. describe('for SEK currency', function () {
  220. beforeEach(function () {
  221. this.mockRecommendedCurrency = 'SEK'
  222. this.recommendedCurrencySymbol =
  223. this.settings.groupPlanModalOptions.currencySymbols[
  224. this.mockRecommendedCurrency
  225. ]
  226. this.expectedPrice =
  227. this.GroupPlansData.enterprise[this.plan][
  228. this.mockRecommendedCurrency
  229. ][this.INITIAL_LICENSE_SIZE].price_in_cents / 100
  230. })
  231. it('should return the correct localized price', function () {
  232. const expectedLocalizedPrice = `${this.expectedPrice} ${this.recommendedCurrencySymbol}`
  233. const {
  234. price: { collaborator },
  235. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  236. this.mockRecommendedCurrency
  237. )
  238. expect(collaborator).to.be.equal(expectedLocalizedPrice)
  239. })
  240. it('should return the correct localized price per user', function () {
  241. const expectedPricePerUser =
  242. this.expectedPrice / this.INITIAL_LICENSE_SIZE
  243. const expectedLocalizedPricePerUser = `${expectedPricePerUser} ${this.recommendedCurrencySymbol}`
  244. const {
  245. pricePerUser: { collaborator },
  246. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  247. this.mockRecommendedCurrency
  248. )
  249. expect(collaborator).to.be.equal(expectedLocalizedPricePerUser)
  250. })
  251. })
  252. describe('for NOK currency', function () {
  253. beforeEach(function () {
  254. this.mockRecommendedCurrency = 'NOK'
  255. this.recommendedCurrencySymbol =
  256. this.settings.groupPlanModalOptions.currencySymbols[
  257. this.mockRecommendedCurrency
  258. ]
  259. this.expectedPrice =
  260. this.GroupPlansData.enterprise[this.plan][
  261. this.mockRecommendedCurrency
  262. ][this.INITIAL_LICENSE_SIZE].price_in_cents / 100
  263. })
  264. it('should return the correct localized price', function () {
  265. const expectedLocalizedPrice = `${this.expectedPrice} ${this.recommendedCurrencySymbol}`
  266. const {
  267. price: { collaborator },
  268. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  269. this.mockRecommendedCurrency
  270. )
  271. expect(collaborator).to.be.equal(expectedLocalizedPrice)
  272. })
  273. it('should return the correct localized price per user', function () {
  274. const expectedPricePerUser =
  275. this.expectedPrice / this.INITIAL_LICENSE_SIZE
  276. const expectedLocalizedPricePerUser = `${expectedPricePerUser} ${this.recommendedCurrencySymbol}`
  277. const {
  278. pricePerUser: { collaborator },
  279. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  280. this.mockRecommendedCurrency
  281. )
  282. expect(collaborator).to.be.equal(expectedLocalizedPricePerUser)
  283. })
  284. })
  285. describe('for other supported currencies', function () {
  286. beforeEach(function () {
  287. this.mockRecommendedCurrency = 'USD'
  288. this.recommendedCurrencySymbol =
  289. this.settings.groupPlanModalOptions.currencySymbols[
  290. this.mockRecommendedCurrency
  291. ]
  292. this.expectedPrice =
  293. this.GroupPlansData.enterprise[this.plan][
  294. this.mockRecommendedCurrency
  295. ][this.INITIAL_LICENSE_SIZE].price_in_cents / 100
  296. })
  297. it('should return the correct localized price', function () {
  298. const expectedLocalizedPrice = `${this.recommendedCurrencySymbol}${this.expectedPrice}`
  299. const {
  300. price: { collaborator },
  301. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  302. this.mockRecommendedCurrency
  303. )
  304. expect(collaborator).to.be.equal(expectedLocalizedPrice)
  305. })
  306. it('should return the correct localized price per user', function () {
  307. const expectedPricePerUser =
  308. this.expectedPrice / this.INITIAL_LICENSE_SIZE
  309. const expectedLocalizedPricePerUser = `${this.recommendedCurrencySymbol}${expectedPricePerUser}`
  310. const {
  311. pricePerUser: { collaborator },
  312. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  313. this.mockRecommendedCurrency
  314. )
  315. expect(collaborator).to.be.equal(expectedLocalizedPricePerUser)
  316. })
  317. })
  318. })
  319. describe('professional plan plan', function () {
  320. beforeEach(function () {
  321. this.plan = 'professional'
  322. })
  323. describe('for CHF currency', function () {
  324. beforeEach(function () {
  325. this.mockRecommendedCurrency = 'CHF'
  326. this.recommendedCurrencySymbol =
  327. this.settings.groupPlanModalOptions.currencySymbols[
  328. this.mockRecommendedCurrency
  329. ]
  330. this.expectedPrice =
  331. this.GroupPlansData.enterprise[this.plan][
  332. this.mockRecommendedCurrency
  333. ][this.INITIAL_LICENSE_SIZE].price_in_cents / 100
  334. })
  335. it('should return the correct localized price', function () {
  336. const expectedLocalizedPrice = `${this.recommendedCurrencySymbol} ${this.expectedPrice}`
  337. const {
  338. price: { professional },
  339. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  340. this.mockRecommendedCurrency
  341. )
  342. expect(professional).to.be.equal(expectedLocalizedPrice)
  343. })
  344. it('should return the correct localized price per user', function () {
  345. const expectedPricePerUser =
  346. this.expectedPrice / this.INITIAL_LICENSE_SIZE
  347. const expectedLocalizedPricePerUser = `${this.recommendedCurrencySymbol} ${expectedPricePerUser}`
  348. const {
  349. pricePerUser: { professional },
  350. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  351. this.mockRecommendedCurrency
  352. )
  353. expect(professional).to.be.equal(expectedLocalizedPricePerUser)
  354. })
  355. })
  356. describe('for DKK currency', function () {
  357. beforeEach(function () {
  358. this.mockRecommendedCurrency = 'DKK'
  359. this.recommendedCurrencySymbol =
  360. this.settings.groupPlanModalOptions.currencySymbols[
  361. this.mockRecommendedCurrency
  362. ]
  363. this.expectedPrice =
  364. this.GroupPlansData.enterprise[this.plan][
  365. this.mockRecommendedCurrency
  366. ][this.INITIAL_LICENSE_SIZE].price_in_cents / 100
  367. })
  368. it('should return the correct localized price', function () {
  369. const expectedLocalizedPrice = `${this.expectedPrice} ${this.recommendedCurrencySymbol}`
  370. const {
  371. price: { professional },
  372. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  373. this.mockRecommendedCurrency
  374. )
  375. expect(professional).to.be.equal(expectedLocalizedPrice)
  376. })
  377. it('should return the correct localized price per user', function () {
  378. const expectedPricePerUser =
  379. this.expectedPrice / this.INITIAL_LICENSE_SIZE
  380. const expectedLocalizedPricePerUser = `${expectedPricePerUser} ${this.recommendedCurrencySymbol}`
  381. const {
  382. pricePerUser: { professional },
  383. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  384. this.mockRecommendedCurrency
  385. )
  386. expect(professional).to.be.equal(expectedLocalizedPricePerUser)
  387. })
  388. })
  389. describe('for SEK currency', function () {
  390. beforeEach(function () {
  391. this.mockRecommendedCurrency = 'SEK'
  392. this.recommendedCurrencySymbol =
  393. this.settings.groupPlanModalOptions.currencySymbols[
  394. this.mockRecommendedCurrency
  395. ]
  396. this.expectedPrice =
  397. this.GroupPlansData.enterprise[this.plan][
  398. this.mockRecommendedCurrency
  399. ][this.INITIAL_LICENSE_SIZE].price_in_cents / 100
  400. })
  401. it('should return the correct localized price', function () {
  402. const expectedLocalizedPrice = `${this.expectedPrice} ${this.recommendedCurrencySymbol}`
  403. const {
  404. price: { professional },
  405. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  406. this.mockRecommendedCurrency
  407. )
  408. expect(professional).to.be.equal(expectedLocalizedPrice)
  409. })
  410. it('should return the correct localized price per user', function () {
  411. const expectedPricePerUser =
  412. this.expectedPrice / this.INITIAL_LICENSE_SIZE
  413. const expectedLocalizedPricePerUser = `${expectedPricePerUser} ${this.recommendedCurrencySymbol}`
  414. const {
  415. pricePerUser: { professional },
  416. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  417. this.mockRecommendedCurrency
  418. )
  419. expect(professional).to.be.equal(expectedLocalizedPricePerUser)
  420. })
  421. })
  422. describe('for NOK currency', function () {
  423. beforeEach(function () {
  424. this.mockRecommendedCurrency = 'NOK'
  425. this.recommendedCurrencySymbol =
  426. this.settings.groupPlanModalOptions.currencySymbols[
  427. this.mockRecommendedCurrency
  428. ]
  429. this.expectedPrice =
  430. this.GroupPlansData.enterprise[this.plan][
  431. this.mockRecommendedCurrency
  432. ][this.INITIAL_LICENSE_SIZE].price_in_cents / 100
  433. })
  434. it('should return the correct localized price', function () {
  435. const expectedLocalizedPrice = `${this.expectedPrice} ${this.recommendedCurrencySymbol}`
  436. const {
  437. price: { professional },
  438. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  439. this.mockRecommendedCurrency
  440. )
  441. expect(professional).to.be.equal(expectedLocalizedPrice)
  442. })
  443. it('should return the correct localized price per user', function () {
  444. const expectedPricePerUser =
  445. this.expectedPrice / this.INITIAL_LICENSE_SIZE
  446. const expectedLocalizedPricePerUser = `${expectedPricePerUser} ${this.recommendedCurrencySymbol}`
  447. const {
  448. pricePerUser: { professional },
  449. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  450. this.mockRecommendedCurrency
  451. )
  452. expect(professional).to.be.equal(expectedLocalizedPricePerUser)
  453. })
  454. })
  455. describe('for other supported currencies', function () {
  456. beforeEach(function () {
  457. this.mockRecommendedCurrency = 'USD'
  458. this.recommendedCurrencySymbol =
  459. this.settings.groupPlanModalOptions.currencySymbols[
  460. this.mockRecommendedCurrency
  461. ]
  462. this.expectedPrice =
  463. this.GroupPlansData.enterprise[this.plan][
  464. this.mockRecommendedCurrency
  465. ][this.INITIAL_LICENSE_SIZE].price_in_cents / 100
  466. })
  467. it('should return the correct localized price', function () {
  468. const expectedLocalizedPrice = `${this.recommendedCurrencySymbol}${this.expectedPrice}`
  469. const {
  470. price: { professional },
  471. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  472. this.mockRecommendedCurrency
  473. )
  474. expect(professional).to.be.equal(expectedLocalizedPrice)
  475. })
  476. it('should return the correct localized price per user', function () {
  477. const expectedPricePerUser =
  478. this.expectedPrice / this.INITIAL_LICENSE_SIZE
  479. const expectedLocalizedPricePerUser = `${this.recommendedCurrencySymbol}${expectedPricePerUser}`
  480. const {
  481. pricePerUser: { professional },
  482. } = this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
  483. this.mockRecommendedCurrency
  484. )
  485. expect(professional).to.be.equal(expectedLocalizedPricePerUser)
  486. })
  487. })
  488. })
  489. })
  490. })