current-plan-widget.test.tsx 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. import { expect } from 'chai'
  2. import sinon from 'sinon'
  3. import { fireEvent, render, screen } from '@testing-library/react'
  4. import {
  5. CommonsPlanSubscription,
  6. GroupPlanSubscription,
  7. IndividualPlanSubscription,
  8. } from '../../../../../types/project/dashboard/subscription'
  9. import { DeepReadonly } from '../../../../../types/utils'
  10. import * as eventTracking from '../../../../../frontend/js/infrastructure/event-tracking'
  11. import CurrentPlanWidget from '../../../../../frontend/js/features/project-list/components/current-plan-widget/current-plan-widget'
  12. describe('<CurrentPlanWidget />', function () {
  13. const freePlanTooltipMessage =
  14. /click to find out how you could benefit from overleaf premium features/i
  15. const paidPlanTooltipMessage =
  16. /click to find out how to make the most of your overleaf premium features/i
  17. beforeEach(function () {
  18. window.metaAttributesCache = window.metaAttributesCache || new Map()
  19. })
  20. describe('free plan', function () {
  21. let sendMBSpy: sinon.SinonSpy
  22. beforeEach(function () {
  23. sendMBSpy = sinon.spy(eventTracking, 'sendMB')
  24. window.metaAttributesCache.set('ol-usersBestSubscription', {
  25. type: 'free',
  26. })
  27. render(<CurrentPlanWidget />)
  28. })
  29. afterEach(function () {
  30. sendMBSpy.restore()
  31. })
  32. it('shows text and tooltip on mouseover', function () {
  33. const link = screen.getByRole('link', {
  34. name: /you’re on the free plan/i,
  35. })
  36. fireEvent.mouseOver(link)
  37. screen.getByRole('tooltip', { name: freePlanTooltipMessage })
  38. })
  39. it('clicks on upgrade button', function () {
  40. const upgradeLink = screen.getByRole('link', { name: /upgrade/i })
  41. fireEvent.click(upgradeLink)
  42. expect(sendMBSpy).to.be.calledOnce
  43. expect(sendMBSpy).calledWith('upgrade-button-click', {
  44. source: 'dashboard-top',
  45. page: '/',
  46. 'project-dashboard-react': 'enabled',
  47. 'is-dashboard-sidebar-hidden': false,
  48. 'is-screen-width-less-than-768px': false,
  49. })
  50. })
  51. })
  52. describe('paid plan', function () {
  53. describe('trial', function () {
  54. const subscription = {
  55. type: 'individual',
  56. plan: {
  57. name: 'Abc',
  58. },
  59. subscription: {
  60. name: 'Example Name',
  61. },
  62. remainingTrialDays: -1,
  63. } as DeepReadonly<IndividualPlanSubscription>
  64. beforeEach(function () {
  65. window.metaAttributesCache.set('ol-usersBestSubscription', {
  66. ...subscription,
  67. })
  68. })
  69. it('shows remaining days', function () {
  70. const newSubscription: IndividualPlanSubscription = {
  71. ...subscription,
  72. remainingTrialDays: 5,
  73. }
  74. window.metaAttributesCache.set(
  75. 'ol-usersBestSubscription',
  76. newSubscription
  77. )
  78. render(<CurrentPlanWidget />)
  79. screen.getByRole('link', {
  80. name: new RegExp(
  81. `${newSubscription.remainingTrialDays} more days on your overleaf premium trial`,
  82. 'i'
  83. ),
  84. })
  85. })
  86. it('shows last day message', function () {
  87. window.metaAttributesCache.set('ol-usersBestSubscription', {
  88. ...subscription,
  89. remainingTrialDays: 1,
  90. })
  91. render(<CurrentPlanWidget />)
  92. screen.getByRole('link', {
  93. name: /this is the last day of your overleaf premium trial/i,
  94. })
  95. })
  96. })
  97. describe('individual', function () {
  98. const subscription = {
  99. type: 'individual',
  100. plan: {
  101. name: 'Abc',
  102. },
  103. subscription: {
  104. teamName: 'Example Team',
  105. name: 'Example Name',
  106. },
  107. remainingTrialDays: -1,
  108. } as DeepReadonly<IndividualPlanSubscription>
  109. beforeEach(function () {
  110. window.metaAttributesCache.set('ol-usersBestSubscription', {
  111. ...subscription,
  112. })
  113. })
  114. it('shows text and tooltip on mouseover', function () {
  115. render(<CurrentPlanWidget />)
  116. const link = screen.getByRole('link', {
  117. name: /you’re using overleaf premium/i,
  118. })
  119. fireEvent.mouseOver(link)
  120. screen.getByRole('tooltip', {
  121. name: new RegExp(`on the ${subscription.plan.name}`, 'i'),
  122. })
  123. screen.getByRole('tooltip', { name: paidPlanTooltipMessage })
  124. })
  125. })
  126. describe('group', function () {
  127. const subscription = {
  128. type: 'group',
  129. plan: {
  130. name: 'Abc',
  131. },
  132. subscription: {
  133. name: 'Example Name',
  134. },
  135. remainingTrialDays: -1,
  136. } as DeepReadonly<GroupPlanSubscription>
  137. beforeEach(function () {
  138. window.metaAttributesCache.set('ol-usersBestSubscription', {
  139. ...subscription,
  140. })
  141. })
  142. it('shows text and tooltip on mouseover (without subscription team name)', function () {
  143. render(<CurrentPlanWidget />)
  144. const link = screen.getByRole('link', {
  145. name: /you’re using overleaf premium/i,
  146. })
  147. fireEvent.mouseOver(link)
  148. expect(subscription.subscription.teamName).to.be.undefined
  149. screen.getByRole('tooltip', {
  150. name: new RegExp(
  151. `on the ${subscription.plan.name} plan as a member of a group subscription`,
  152. 'i'
  153. ),
  154. })
  155. screen.getByRole('tooltip', { name: paidPlanTooltipMessage })
  156. })
  157. it('shows text and tooltip on mouseover (with subscription team name)', function () {
  158. const newSubscription = {
  159. ...subscription,
  160. subscription: {
  161. teamName: 'Example Team',
  162. },
  163. }
  164. window.metaAttributesCache.set(
  165. 'ol-usersBestSubscription',
  166. newSubscription
  167. )
  168. render(<CurrentPlanWidget />)
  169. const link = screen.getByRole('link', {
  170. name: /you’re using overleaf premium/i,
  171. })
  172. fireEvent.mouseOver(link)
  173. screen.getByRole('tooltip', {
  174. name: new RegExp(
  175. `on the ${newSubscription.plan.name} plan as a member of a group subscription, ${newSubscription.subscription.teamName}`,
  176. 'i'
  177. ),
  178. })
  179. screen.getByRole('tooltip', { name: paidPlanTooltipMessage })
  180. })
  181. })
  182. describe('commons', function () {
  183. it('shows text and tooltip on mouseover', function () {
  184. const subscription = {
  185. type: 'commons',
  186. plan: {
  187. name: 'Abc',
  188. },
  189. subscription: {
  190. name: 'Example Name',
  191. },
  192. } as DeepReadonly<CommonsPlanSubscription>
  193. window.metaAttributesCache.set('ol-usersBestSubscription', {
  194. ...subscription,
  195. })
  196. render(<CurrentPlanWidget />)
  197. const link = screen.getByRole('link', {
  198. name: /you’re using overleaf premium/i,
  199. })
  200. fireEvent.mouseOver(link)
  201. screen.getByRole('tooltip', {
  202. name: new RegExp(
  203. `on the ${subscription.plan.name} plan because of your affiliation with ${subscription.subscription.name}`,
  204. 'i'
  205. ),
  206. })
  207. screen.getByRole('tooltip', { name: paidPlanTooltipMessage })
  208. })
  209. })
  210. })
  211. describe('features page split test', function () {
  212. let sendMBSpy: sinon.SinonSpy
  213. const variants = [
  214. { name: 'default', link: '/learn/how-to/Overleaf_premium_features' },
  215. { name: 'new', link: '/about/features-overview' },
  216. ]
  217. const plans = [
  218. { type: 'free' },
  219. {
  220. type: 'individual',
  221. plan: {
  222. name: 'Abc',
  223. },
  224. },
  225. {
  226. type: 'group',
  227. plan: {
  228. name: 'Abc',
  229. },
  230. subscription: {
  231. teamName: 'Example Team',
  232. name: 'Example Name',
  233. },
  234. },
  235. {
  236. type: 'commons',
  237. plan: {
  238. name: 'Abc',
  239. },
  240. subscription: {
  241. name: 'Example Name',
  242. },
  243. },
  244. ]
  245. beforeEach(function () {
  246. sendMBSpy = sinon.spy(eventTracking, 'sendMB')
  247. })
  248. afterEach(function () {
  249. sendMBSpy.restore()
  250. })
  251. for (const variant of variants) {
  252. describe(`${variant.name} variant`, function () {
  253. beforeEach(function () {
  254. window.metaAttributesCache.set('ol-splitTestVariants', {
  255. 'features-page': variant.name,
  256. })
  257. })
  258. afterEach(function () {
  259. window.metaAttributesCache.delete('ol-splitTestVariants')
  260. })
  261. for (const plan of plans) {
  262. it(`links to ${variant.name} features page on ${plan.type} plan and sends analytics event`, function () {
  263. window.metaAttributesCache.set('ol-usersBestSubscription', {
  264. ...plan,
  265. })
  266. render(<CurrentPlanWidget />)
  267. const links = screen.getAllByRole('link')
  268. expect(links[0].getAttribute('href')).to.equal(variant.link)
  269. fireEvent.click(links[0])
  270. expect(sendMBSpy).to.be.calledOnce
  271. expect(sendMBSpy).calledWith('features-page-link', {
  272. splitTest: 'features-page',
  273. splitTestVariant: variant.name,
  274. page: '/',
  275. })
  276. window.metaAttributesCache.delete('ol-usersBestSubscription')
  277. })
  278. }
  279. })
  280. }
  281. })
  282. })