AnalyticsUTMTrackingMiddleware.test.mjs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import { vi } from 'vitest'
  2. import sinon from 'sinon'
  3. import MockRequest from '../helpers/MockRequest.js'
  4. import MockResponse from '../helpers/MockResponse.js'
  5. import { assert } from 'chai'
  6. const MODULE_PATH = new URL(
  7. '../../../../app/src/Features/Analytics/AnalyticsUTMTrackingMiddleware',
  8. import.meta.url
  9. ).pathname
  10. describe('AnalyticsUTMTrackingMiddleware', function () {
  11. beforeEach(async function (ctx) {
  12. ctx.analyticsId = 'ecdb935a-52f3-4f91-aebc-7a70d2ffbb55'
  13. ctx.userId = '61795fcb013504bb7b663092'
  14. ctx.req = new MockRequest()
  15. ctx.res = new MockResponse()
  16. ctx.next = sinon.stub().returns()
  17. ctx.req.session = {
  18. user: {
  19. _id: ctx.userId,
  20. analyticsId: ctx.analyticsId,
  21. },
  22. }
  23. vi.doMock(
  24. '../../../../app/src/Features/Analytics/AnalyticsManager.js',
  25. () => ({
  26. default: (ctx.AnalyticsManager = {
  27. recordEventForSession: sinon.stub().resolves(),
  28. setUserPropertyForSessionInBackground: sinon.stub(),
  29. }),
  30. })
  31. )
  32. vi.doMock('@overleaf/settings', () => ({
  33. default: {
  34. siteUrl: 'https://www.overleaf.com',
  35. },
  36. }))
  37. ctx.AnalyticsUTMTrackingMiddleware = (await import(MODULE_PATH)).default
  38. ctx.middleware = ctx.AnalyticsUTMTrackingMiddleware.recordUTMTags()
  39. })
  40. describe('without UTM tags in query', function () {
  41. beforeEach(function (ctx) {
  42. ctx.req.url = '/project'
  43. ctx.middleware(ctx.req, ctx.res, ctx.next)
  44. })
  45. it('user is not redirected', function (ctx) {
  46. assert.isFalse(ctx.res.redirected)
  47. })
  48. it('next middleware is executed', function (ctx) {
  49. sinon.assert.calledOnce(ctx.next)
  50. })
  51. it('no event or user property is recorded', function (ctx) {
  52. sinon.assert.notCalled(ctx.AnalyticsManager.recordEventForSession)
  53. sinon.assert.notCalled(
  54. ctx.AnalyticsManager.setUserPropertyForSessionInBackground
  55. )
  56. })
  57. })
  58. describe('with all UTM tags in query', function () {
  59. beforeEach(function (ctx) {
  60. ctx.req.url =
  61. '/project?utm_source=Organic&utm_medium=Facebook&utm_campaign=Some%20Campaign&utm_content=foo-bar&utm_term=overridden'
  62. ctx.req.query = {
  63. utm_source: 'Organic',
  64. utm_medium: 'Facebook',
  65. utm_campaign: 'Some Campaign',
  66. utm_content: 'foo-bar',
  67. utm_term: 'overridden',
  68. }
  69. ctx.middleware(ctx.req, ctx.res, ctx.next)
  70. })
  71. it('user is redirected', function (ctx) {
  72. assert.isTrue(ctx.res.redirected)
  73. assert.equal('/project', ctx.res.redirectedTo)
  74. })
  75. it('next middleware is not executed', function (ctx) {
  76. sinon.assert.notCalled(ctx.next)
  77. })
  78. it('page-view event is recorded for session', function (ctx) {
  79. sinon.assert.calledWith(
  80. ctx.AnalyticsManager.recordEventForSession,
  81. ctx.req.session,
  82. 'page-view',
  83. {
  84. path: '/project',
  85. utm_source: 'Organic',
  86. utm_medium: 'Facebook',
  87. utm_campaign: 'Some Campaign',
  88. utm_content: 'foo-bar',
  89. utm_term: 'overridden',
  90. }
  91. )
  92. })
  93. it('utm-tags user property is set for session', function (ctx) {
  94. sinon.assert.calledWith(
  95. ctx.AnalyticsManager.setUserPropertyForSessionInBackground,
  96. ctx.req.session,
  97. 'utm-tags',
  98. 'Organic;Facebook;Some Campaign;foo-bar'
  99. )
  100. })
  101. })
  102. describe('with some UTM tags in query', function () {
  103. beforeEach(function (ctx) {
  104. ctx.req.url =
  105. '/project?utm_medium=Facebook&utm_campaign=Some%20Campaign&utm_term=foo'
  106. ctx.req.query = {
  107. utm_medium: 'Facebook',
  108. utm_campaign: 'Some Campaign',
  109. utm_term: 'foo',
  110. }
  111. ctx.middleware(ctx.req, ctx.res, ctx.next)
  112. })
  113. it('user is redirected', function (ctx) {
  114. assert.isTrue(ctx.res.redirected)
  115. assert.equal('/project', ctx.res.redirectedTo)
  116. })
  117. it('next middleware is not executed', function (ctx) {
  118. sinon.assert.notCalled(ctx.next)
  119. })
  120. it('page-view event is recorded for session', function (ctx) {
  121. sinon.assert.calledWith(
  122. ctx.AnalyticsManager.recordEventForSession,
  123. ctx.req.session,
  124. 'page-view',
  125. {
  126. path: '/project',
  127. utm_medium: 'Facebook',
  128. utm_campaign: 'Some Campaign',
  129. utm_term: 'foo',
  130. }
  131. )
  132. })
  133. it('utm-tags user property is set for session', function (ctx) {
  134. sinon.assert.calledWith(
  135. ctx.AnalyticsManager.setUserPropertyForSessionInBackground,
  136. ctx.req.session,
  137. 'utm-tags',
  138. 'N/A;Facebook;Some Campaign;foo'
  139. )
  140. })
  141. })
  142. describe('with some UTM tags and additional parameters in query', function () {
  143. beforeEach(function (ctx) {
  144. ctx.req.url =
  145. '/project?utm_medium=Facebook&utm_campaign=Some%20Campaign&other_param=some-value'
  146. ctx.req.query = {
  147. utm_medium: 'Facebook',
  148. utm_campaign: 'Some Campaign',
  149. other_param: 'some-value',
  150. }
  151. ctx.middleware(ctx.req, ctx.res, ctx.next)
  152. })
  153. it('user is redirected', function (ctx) {
  154. assert.isTrue(ctx.res.redirected)
  155. assert.equal('/project?other_param=some-value', ctx.res.redirectedTo)
  156. })
  157. it('next middleware is not executed', function (ctx) {
  158. sinon.assert.notCalled(ctx.next)
  159. })
  160. it('page-view event is recorded for session', function (ctx) {
  161. sinon.assert.calledWith(
  162. ctx.AnalyticsManager.recordEventForSession,
  163. ctx.req.session,
  164. 'page-view',
  165. {
  166. path: '/project',
  167. utm_medium: 'Facebook',
  168. utm_campaign: 'Some Campaign',
  169. }
  170. )
  171. })
  172. it('utm-tags user property is set for session', function (ctx) {
  173. sinon.assert.calledWith(
  174. ctx.AnalyticsManager.setUserPropertyForSessionInBackground,
  175. ctx.req.session,
  176. 'utm-tags',
  177. 'N/A;Facebook;Some Campaign;N/A'
  178. )
  179. })
  180. })
  181. })