utneque 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (431) hide show
  1. package/.editorconfig +10 -0
  2. package/.eslintrc.isomorphic.js +26 -0
  3. package/.eslintrc.js +86 -0
  4. package/.gitattributes +1 -0
  5. package/.github/workflows/ci.yml +33 -0
  6. package/.github/workflows/deploy-browser-cdn-candidate.yml +51 -0
  7. package/.github/workflows/deploy-releases.yml +178 -0
  8. package/.nvmrc +1 -0
  9. package/.prettierrc +7 -0
  10. package/.vscode/extensions.json +3 -0
  11. package/.vscode/launch.json +81 -0
  12. package/.vscode/settings.json +41 -0
  13. package/.yarn/plugins/@yarnpkg/plugin-constraints.cjs +52 -0
  14. package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +546 -0
  15. package/.yarn/plugins/@yarnpkg/plugin-typescript.cjs +9 -0
  16. package/.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs +28 -0
  17. package/.yarn/releases/yarn-3.4.1.cjs +873 -0
  18. package/.yarnrc.yml +15 -0
  19. package/jest.config.js +18 -0
  20. package/package.json +59 -0
  21. package/packages/browser/.eslintrc.js +9 -0
  22. package/packages/browser/.lintstagedrc.js +1 -0
  23. package/packages/browser/ARCHITECTURE.md +48 -0
  24. package/packages/browser/LICENSE.MD +45 -0
  25. package/packages/browser/Makefile +64 -0
  26. package/packages/browser/README.md +227 -0
  27. package/packages/browser/e2e-tests/local-server.ts +28 -0
  28. package/packages/browser/e2e-tests/performance/ajs-perf-browser.test.ts +75 -0
  29. package/packages/browser/jest.config.js +15 -0
  30. package/packages/browser/jest.setup.js +10 -0
  31. package/packages/browser/package.json +106 -0
  32. package/packages/browser/qa/README.md +41 -0
  33. package/packages/browser/qa/__fixtures__/snippets.ts +148 -0
  34. package/packages/browser/qa/__tests__/backwards-compatibility.test.ts +180 -0
  35. package/packages/browser/qa/__tests__/destinations.test.ts +101 -0
  36. package/packages/browser/qa/__tests__/smoke.test.ts +170 -0
  37. package/packages/browser/qa/lib/benchmark.ts +36 -0
  38. package/packages/browser/qa/lib/browser.ts +28 -0
  39. package/packages/browser/qa/lib/jest-reporter.js +57 -0
  40. package/packages/browser/qa/lib/runner.ts +142 -0
  41. package/packages/browser/qa/lib/schema.ts +59 -0
  42. package/packages/browser/qa/lib/server.ts +54 -0
  43. package/packages/browser/qa/lib/stats.ts +52 -0
  44. package/packages/browser/scripts/build-prep.sh +7 -0
  45. package/packages/browser/scripts/ci.sh +15 -0
  46. package/packages/browser/scripts/release.js +121 -0
  47. package/packages/browser/scripts/release.sh +9 -0
  48. package/packages/browser/scripts/run.sh +8 -0
  49. package/packages/browser/src/browser/__tests__/analytics-lazy-init.integration.test.ts +51 -0
  50. package/packages/browser/src/browser/__tests__/analytics-pre-init.integration.test.ts +440 -0
  51. package/packages/browser/src/browser/__tests__/anon-id-and-reset.integration.test.ts +73 -0
  52. package/packages/browser/src/browser/__tests__/cdn.test.ts +53 -0
  53. package/packages/browser/src/browser/__tests__/csp-detection.test.ts +140 -0
  54. package/packages/browser/src/browser/__tests__/inspector.integration.test.ts +121 -0
  55. package/packages/browser/src/browser/__tests__/integration.test.ts +1213 -0
  56. package/packages/browser/src/browser/__tests__/integrations.integration.test.ts +260 -0
  57. package/packages/browser/src/browser/__tests__/page-enrichment.integration.test.ts +216 -0
  58. package/packages/browser/src/browser/__tests__/query-string.integration.test.ts +116 -0
  59. package/packages/browser/src/browser/__tests__/standalone-analytics.test.ts +303 -0
  60. package/packages/browser/src/browser/__tests__/standalone-errors.test.ts +136 -0
  61. package/packages/browser/src/browser/__tests__/standalone.test.ts +97 -0
  62. package/packages/browser/src/browser/__tests__/typedef-tests/analytics-browser.ts +150 -0
  63. package/packages/browser/src/browser/__tests__/update-cdn-settings.test.ts +71 -0
  64. package/packages/browser/src/browser/browser-umd.ts +19 -0
  65. package/packages/browser/src/browser/index.ts +486 -0
  66. package/packages/browser/src/browser/standalone-analytics.ts +62 -0
  67. package/packages/browser/src/browser/standalone-interface.ts +11 -0
  68. package/packages/browser/src/browser/standalone.ts +92 -0
  69. package/packages/browser/src/core/__tests__/track-form.test.ts +193 -0
  70. package/packages/browser/src/core/__tests__/track-link.test.ts +252 -0
  71. package/packages/browser/src/core/analytics/__tests__/integration.test.ts +334 -0
  72. package/packages/browser/src/core/analytics/__tests__/test-plugins.ts +94 -0
  73. package/packages/browser/src/core/analytics/index.ts +672 -0
  74. package/packages/browser/src/core/analytics/interfaces.ts +100 -0
  75. package/packages/browser/src/core/arguments-resolver/__tests__/index.test.ts +524 -0
  76. package/packages/browser/src/core/arguments-resolver/index.ts +200 -0
  77. package/packages/browser/src/core/auto-track.ts +152 -0
  78. package/packages/browser/src/core/buffer/__tests__/index.test.ts +455 -0
  79. package/packages/browser/src/core/buffer/index.ts +371 -0
  80. package/packages/browser/src/core/callback/index.ts +1 -0
  81. package/packages/browser/src/core/connection/__tests__/index.test.ts +25 -0
  82. package/packages/browser/src/core/connection/index.ts +13 -0
  83. package/packages/browser/src/core/constants/index.ts +1 -0
  84. package/packages/browser/src/core/context/__tests__/index.test.ts +201 -0
  85. package/packages/browser/src/core/context/index.ts +21 -0
  86. package/packages/browser/src/core/environment/index.ts +7 -0
  87. package/packages/browser/src/core/events/__tests__/index.test.ts +450 -0
  88. package/packages/browser/src/core/events/index.ts +280 -0
  89. package/packages/browser/src/core/events/interfaces.ts +36 -0
  90. package/packages/browser/src/core/inspector/index.ts +14 -0
  91. package/packages/browser/src/core/page/__tests__/index.test.ts +130 -0
  92. package/packages/browser/src/core/page/add-page-context.ts +33 -0
  93. package/packages/browser/src/core/page/get-page-context.ts +140 -0
  94. package/packages/browser/src/core/page/index.ts +2 -0
  95. package/packages/browser/src/core/plugin/index.ts +12 -0
  96. package/packages/browser/src/core/query-string/__tests__/gracefulDecodeURIComponent.test.ts +17 -0
  97. package/packages/browser/src/core/query-string/__tests__/index.test.ts +149 -0
  98. package/packages/browser/src/core/query-string/__tests__/pickPrefix.test.ts +31 -0
  99. package/packages/browser/src/core/query-string/__tests__/useQueryString.test.ts +60 -0
  100. package/packages/browser/src/core/query-string/gracefulDecodeURIComponent.ts +16 -0
  101. package/packages/browser/src/core/query-string/index.ts +64 -0
  102. package/packages/browser/src/core/query-string/pickPrefix.ts +20 -0
  103. package/packages/browser/src/core/queue/__tests__/event-queue.test.ts +82 -0
  104. package/packages/browser/src/core/queue/event-queue.ts +22 -0
  105. package/packages/browser/src/core/session/__tests__/index.test.ts +41 -0
  106. package/packages/browser/src/core/session/index.ts +107 -0
  107. package/packages/browser/src/core/stats/__tests__/index.test.ts +15 -0
  108. package/packages/browser/src/core/stats/__tests__/remote-metrics.test.ts +189 -0
  109. package/packages/browser/src/core/stats/index.ts +15 -0
  110. package/packages/browser/src/core/stats/remote-metrics.ts +144 -0
  111. package/packages/browser/src/core/storage/__tests__/cookieStorage.test.ts +58 -0
  112. package/packages/browser/src/core/storage/__tests__/localStorage.test.ts +70 -0
  113. package/packages/browser/src/core/storage/__tests__/test-helpers.ts +26 -0
  114. package/packages/browser/src/core/storage/__tests__/universalStorage.test.ts +167 -0
  115. package/packages/browser/src/core/storage/cookieStorage.ts +80 -0
  116. package/packages/browser/src/core/storage/index.ts +64 -0
  117. package/packages/browser/src/core/storage/localStorage.ts +45 -0
  118. package/packages/browser/src/core/storage/memoryStorage.ts +22 -0
  119. package/packages/browser/src/core/storage/settings.ts +23 -0
  120. package/packages/browser/src/core/storage/types.ts +49 -0
  121. package/packages/browser/src/core/storage/universalStorage.ts +78 -0
  122. package/packages/browser/src/core/user/__tests__/index.test.ts +922 -0
  123. package/packages/browser/src/core/user/__tests__/migrate.test.ts +101 -0
  124. package/packages/browser/src/core/user/__tests__/session.test.ts +136 -0
  125. package/packages/browser/src/core/user/__tests__/tld.test.ts +36 -0
  126. package/packages/browser/src/core/user/index.ts +399 -0
  127. package/packages/browser/src/core/user/migrate.ts +126 -0
  128. package/packages/browser/src/core/user/tld.ts +65 -0
  129. package/packages/browser/src/core/user/vendor/crypto-es/LICENSE +53 -0
  130. package/packages/browser/src/core/user/vendor/crypto-es/lib/aes.ts +302 -0
  131. package/packages/browser/src/core/user/vendor/crypto-es/lib/cipher-core.ts +922 -0
  132. package/packages/browser/src/core/user/vendor/crypto-es/lib/core.ts +806 -0
  133. package/packages/browser/src/core/user/vendor/crypto-es/lib/enc-base64.ts +110 -0
  134. package/packages/browser/src/core/user/vendor/crypto-es/lib/evpkdf.ts +110 -0
  135. package/packages/browser/src/core/user/vendor/crypto-es/lib/md5.ts +238 -0
  136. package/packages/browser/src/core/user/vendor/readme.md +7 -0
  137. package/packages/browser/src/generated/__tests__/version.test.ts +18 -0
  138. package/packages/browser/src/generated/version.ts +2 -0
  139. package/packages/browser/src/index.ts +13 -0
  140. package/packages/browser/src/lib/__tests__/embedded-write-key.test.ts +15 -0
  141. package/packages/browser/src/lib/__tests__/fetch.test.ts +35 -0
  142. package/packages/browser/src/lib/__tests__/get-process-env.test.ts +5 -0
  143. package/packages/browser/src/lib/__tests__/group-by.test.ts +96 -0
  144. package/packages/browser/src/lib/__tests__/is-plan-event-enabled.test.ts +36 -0
  145. package/packages/browser/src/lib/__tests__/is-thenable.test.ts +39 -0
  146. package/packages/browser/src/lib/__tests__/merged-options.test.ts +123 -0
  147. package/packages/browser/src/lib/__tests__/on-page-change.test.ts +74 -0
  148. package/packages/browser/src/lib/__tests__/parse-cdn.test.ts +88 -0
  149. package/packages/browser/src/lib/__tests__/pick.test.ts +34 -0
  150. package/packages/browser/src/lib/__tests__/pick.typedef.ts +39 -0
  151. package/packages/browser/src/lib/bind-all.ts +19 -0
  152. package/packages/browser/src/lib/browser-polyfill.ts +23 -0
  153. package/packages/browser/src/lib/client-hints/__tests__/index.test.ts +66 -0
  154. package/packages/browser/src/lib/client-hints/index.ts +16 -0
  155. package/packages/browser/src/lib/client-hints/interfaces.ts +42 -0
  156. package/packages/browser/src/lib/create-deferred.ts +16 -0
  157. package/packages/browser/src/lib/csp-detection.ts +8 -0
  158. package/packages/browser/src/lib/embedded-write-key.ts +24 -0
  159. package/packages/browser/src/lib/fetch.ts +10 -0
  160. package/packages/browser/src/lib/get-global.ts +16 -0
  161. package/packages/browser/src/lib/get-process-env.ts +11 -0
  162. package/packages/browser/src/lib/global-analytics-helper.ts +31 -0
  163. package/packages/browser/src/lib/group-by.ts +30 -0
  164. package/packages/browser/src/lib/is-plan-event-enabled.ts +20 -0
  165. package/packages/browser/src/lib/is-thenable.ts +9 -0
  166. package/packages/browser/src/lib/load-script.ts +66 -0
  167. package/packages/browser/src/lib/merged-options.ts +46 -0
  168. package/packages/browser/src/lib/on-page-change.ts +29 -0
  169. package/packages/browser/src/lib/p-while.ts +12 -0
  170. package/packages/browser/src/lib/parse-cdn.ts +56 -0
  171. package/packages/browser/src/lib/pick.ts +28 -0
  172. package/packages/browser/src/lib/priority-queue/__tests__/backoff.test.ts +23 -0
  173. package/packages/browser/src/lib/priority-queue/__tests__/index.test.ts +158 -0
  174. package/packages/browser/src/lib/priority-queue/__tests__/persisted.test.ts +228 -0
  175. package/packages/browser/src/lib/priority-queue/backoff.ts +24 -0
  176. package/packages/browser/src/lib/priority-queue/index.ts +6 -0
  177. package/packages/browser/src/lib/priority-queue/persisted.ts +127 -0
  178. package/packages/browser/src/lib/sleep.ts +4 -0
  179. package/packages/browser/src/lib/to-facade.ts +53 -0
  180. package/packages/browser/src/lib/version-type.ts +10 -0
  181. package/packages/browser/src/node/__tests__/node-integration.test.ts +19 -0
  182. package/packages/browser/src/node/index.ts +36 -0
  183. package/packages/browser/src/node/node.browser.ts +7 -0
  184. package/packages/browser/src/plugins/ajs-destination/__tests__/index.test.ts +834 -0
  185. package/packages/browser/src/plugins/ajs-destination/index.ts +392 -0
  186. package/packages/browser/src/plugins/ajs-destination/loader.ts +129 -0
  187. package/packages/browser/src/plugins/ajs-destination/types.ts +44 -0
  188. package/packages/browser/src/plugins/ajs-destination/utils.ts +32 -0
  189. package/packages/browser/src/plugins/analytics-node/__tests__/index.test.ts +69 -0
  190. package/packages/browser/src/plugins/analytics-node/index.ts +67 -0
  191. package/packages/browser/src/plugins/env-enrichment/__tests__/index.test.ts +421 -0
  192. package/packages/browser/src/plugins/env-enrichment/index.ts +208 -0
  193. package/packages/browser/src/plugins/hightouchio/__tests__/batched-dispatcher.test.ts +299 -0
  194. package/packages/browser/src/plugins/hightouchio/__tests__/index.test.ts +317 -0
  195. package/packages/browser/src/plugins/hightouchio/__tests__/normalize.test.ts +181 -0
  196. package/packages/browser/src/plugins/hightouchio/__tests__/retries.test.ts +82 -0
  197. package/packages/browser/src/plugins/hightouchio/batched-dispatcher.ts +127 -0
  198. package/packages/browser/src/plugins/hightouchio/fetch-dispatcher.ts +27 -0
  199. package/packages/browser/src/plugins/hightouchio/index.ts +147 -0
  200. package/packages/browser/src/plugins/hightouchio/normalize.ts +71 -0
  201. package/packages/browser/src/plugins/hightouchio/schedule-flush.ts +58 -0
  202. package/packages/browser/src/plugins/legacy-video-plugins/__tests__/index.test.ts +48 -0
  203. package/packages/browser/src/plugins/legacy-video-plugins/index.ts +16 -0
  204. package/packages/browser/src/plugins/middleware/__tests__/index.test.ts +268 -0
  205. package/packages/browser/src/plugins/middleware/index.ts +131 -0
  206. package/packages/browser/src/plugins/remote-loader/__tests__/index.test.ts +943 -0
  207. package/packages/browser/src/plugins/remote-loader/index.ts +256 -0
  208. package/packages/browser/src/plugins/remote-middleware/__tests__/index.test.ts +116 -0
  209. package/packages/browser/src/plugins/remote-middleware/index.ts +44 -0
  210. package/packages/browser/src/plugins/routing-middleware/__tests__/index.test.ts +64 -0
  211. package/packages/browser/src/plugins/routing-middleware/index.ts +37 -0
  212. package/packages/browser/src/plugins/schema-filter/__tests__/index.test.ts +520 -0
  213. package/packages/browser/src/plugins/schema-filter/index.ts +90 -0
  214. package/packages/browser/src/plugins/validation/__tests__/index.test.ts +78 -0
  215. package/packages/browser/src/plugins/validation/index.ts +44 -0
  216. package/packages/browser/src/test-helpers/browser-storage.ts +75 -0
  217. package/packages/browser/src/test-helpers/factories.ts +18 -0
  218. package/packages/browser/src/test-helpers/fetch-parse.ts +8 -0
  219. package/packages/browser/src/test-helpers/fixtures/cdn-settings.ts +301 -0
  220. package/packages/browser/src/test-helpers/fixtures/classic-destination.ts +25 -0
  221. package/packages/browser/src/test-helpers/fixtures/client-hints.ts +28 -0
  222. package/packages/browser/src/test-helpers/fixtures/create-fetch-method.ts +30 -0
  223. package/packages/browser/src/test-helpers/fixtures/index.ts +4 -0
  224. package/packages/browser/src/test-helpers/fixtures/page-context.ts +11 -0
  225. package/packages/browser/src/test-helpers/test-writekeys.ts +5 -0
  226. package/packages/browser/src/test-helpers/type-assertions.ts +11 -0
  227. package/packages/browser/src/tester/__fixtures__/hightouch-snippet.ts +64 -0
  228. package/packages/browser/src/tester/__fixtures__/index.html +8 -0
  229. package/packages/browser/src/tester/ajs-perf.ts +30 -0
  230. package/packages/browser/src/tester/ajs-tester.ts +119 -0
  231. package/packages/browser/src/tester/server.js +16 -0
  232. package/packages/browser/tsconfig.build.json +9 -0
  233. package/packages/browser/tsconfig.json +13 -0
  234. package/packages/browser/webpack.config.js +106 -0
  235. package/packages/config/package.json +10 -0
  236. package/packages/config/src/index.js +4 -0
  237. package/packages/config/src/jest/config.js +50 -0
  238. package/packages/config/src/jest/get-module-map.js +34 -0
  239. package/packages/config/src/lint-staged/config.js +4 -0
  240. package/packages/config-webpack/package.json +20 -0
  241. package/packages/config-webpack/webpack.config.common.js +75 -0
  242. package/packages/consent/consent-tools/.eslintrc.js +7 -0
  243. package/packages/consent/consent-tools/.lintstagedrc.js +1 -0
  244. package/packages/consent/consent-tools/LICENSE +45 -0
  245. package/packages/consent/consent-tools/README.md +104 -0
  246. package/packages/consent/consent-tools/jest.config.js +6 -0
  247. package/packages/consent/consent-tools/jest.setup.js +4 -0
  248. package/packages/consent/consent-tools/package.json +48 -0
  249. package/packages/consent/consent-tools/src/domain/__tests__/assertions/integrations-assertions.ts +37 -0
  250. package/packages/consent/consent-tools/src/domain/__tests__/consent-stamping.test.ts +45 -0
  251. package/packages/consent/consent-tools/src/domain/__tests__/create-wrapper.test.ts +816 -0
  252. package/packages/consent/consent-tools/src/domain/__tests__/typedef-tests.ts +21 -0
  253. package/packages/consent/consent-tools/src/domain/consent-changed.ts +51 -0
  254. package/packages/consent/consent-tools/src/domain/consent-stamping.ts +19 -0
  255. package/packages/consent/consent-tools/src/domain/create-wrapper.ts +238 -0
  256. package/packages/consent/consent-tools/src/domain/get-initialized-analytics.ts +25 -0
  257. package/packages/consent/consent-tools/src/domain/load-cancellation.ts +31 -0
  258. package/packages/consent/consent-tools/src/domain/validation/__tests__/options-validators.test.ts +77 -0
  259. package/packages/consent/consent-tools/src/domain/validation/__tests__/validation-error.test.ts +15 -0
  260. package/packages/consent/consent-tools/src/domain/validation/common-validators.ts +19 -0
  261. package/packages/consent/consent-tools/src/domain/validation/index.ts +1 -0
  262. package/packages/consent/consent-tools/src/domain/validation/options-validators.ts +74 -0
  263. package/packages/consent/consent-tools/src/domain/validation/validation-error.ts +11 -0
  264. package/packages/consent/consent-tools/src/index.ts +16 -0
  265. package/packages/consent/consent-tools/src/types/errors.ts +14 -0
  266. package/packages/consent/consent-tools/src/types/index.ts +3 -0
  267. package/packages/consent/consent-tools/src/types/settings.ts +121 -0
  268. package/packages/consent/consent-tools/src/types/wrapper.ts +107 -0
  269. package/packages/consent/consent-tools/src/utils/index.ts +4 -0
  270. package/packages/consent/consent-tools/src/utils/pick.ts +18 -0
  271. package/packages/consent/consent-tools/src/utils/pipe.ts +14 -0
  272. package/packages/consent/consent-tools/src/utils/resolve-when.ts +32 -0
  273. package/packages/consent/consent-tools/src/utils/uniq.ts +4 -0
  274. package/packages/consent/consent-tools/tsconfig.build.json +9 -0
  275. package/packages/consent/consent-tools/tsconfig.json +12 -0
  276. package/packages/consent/consent-wrapper-onetrust/.eslintrc.js +7 -0
  277. package/packages/consent/consent-wrapper-onetrust/.lintstagedrc.js +1 -0
  278. package/packages/consent/consent-wrapper-onetrust/LICENSE +45 -0
  279. package/packages/consent/consent-wrapper-onetrust/README.md +125 -0
  280. package/packages/consent/consent-wrapper-onetrust/img/onetrust-cat-id.jpg +0 -0
  281. package/packages/consent/consent-wrapper-onetrust/img/onetrust-popup.jpg +0 -0
  282. package/packages/consent/consent-wrapper-onetrust/jest.config.js +6 -0
  283. package/packages/consent/consent-wrapper-onetrust/jest.setup.js +4 -0
  284. package/packages/consent/consent-wrapper-onetrust/package.json +60 -0
  285. package/packages/consent/consent-wrapper-onetrust/src/domain/__tests__/wrapper.test.ts +151 -0
  286. package/packages/consent/consent-wrapper-onetrust/src/domain/wrapper.ts +61 -0
  287. package/packages/consent/consent-wrapper-onetrust/src/index.ts +6 -0
  288. package/packages/consent/consent-wrapper-onetrust/src/index.umd.ts +11 -0
  289. package/packages/consent/consent-wrapper-onetrust/src/lib/__tests__/onetrust-api.test.ts +181 -0
  290. package/packages/consent/consent-wrapper-onetrust/src/lib/onetrust-api.ts +155 -0
  291. package/packages/consent/consent-wrapper-onetrust/src/lib/validation/index.ts +1 -0
  292. package/packages/consent/consent-wrapper-onetrust/src/lib/validation/onetrust-api-error.ts +11 -0
  293. package/packages/consent/consent-wrapper-onetrust/src/test-helpers/mocks.ts +23 -0
  294. package/packages/consent/consent-wrapper-onetrust/src/test-helpers/onetrust-globals.d.ts +11 -0
  295. package/packages/consent/consent-wrapper-onetrust/src/test-helpers/utils.ts +3 -0
  296. package/packages/consent/consent-wrapper-onetrust/tsconfig.build.json +9 -0
  297. package/packages/consent/consent-wrapper-onetrust/tsconfig.json +11 -0
  298. package/packages/consent/consent-wrapper-onetrust/webpack.config.js +25 -0
  299. package/packages/core/.eslintrc.js +4 -0
  300. package/packages/core/.lintstagedrc.js +1 -0
  301. package/packages/core/LICENSE.MD +45 -0
  302. package/packages/core/README.md +3 -0
  303. package/packages/core/jest.config.js +5 -0
  304. package/packages/core/jest.setup.js +10 -0
  305. package/packages/core/package.json +40 -0
  306. package/packages/core/src/analytics/__tests__/dispatch.test.ts +95 -0
  307. package/packages/core/src/analytics/dispatch.ts +58 -0
  308. package/packages/core/src/analytics/index.ts +11 -0
  309. package/packages/core/src/callback/__tests__/index.test.ts +85 -0
  310. package/packages/core/src/callback/index.ts +51 -0
  311. package/packages/core/src/context/index.ts +123 -0
  312. package/packages/core/src/emitter/__tests__/emitter.test.ts +74 -0
  313. package/packages/core/src/emitter/index.ts +65 -0
  314. package/packages/core/src/emitter/interface.ts +31 -0
  315. package/packages/core/src/events/__tests__/index.test.ts +394 -0
  316. package/packages/core/src/events/index.ts +280 -0
  317. package/packages/core/src/events/interfaces.ts +475 -0
  318. package/packages/core/src/index.ts +19 -0
  319. package/packages/core/src/logger/__tests__/index.test.ts +66 -0
  320. package/packages/core/src/logger/index.ts +74 -0
  321. package/packages/core/src/plugins/index.ts +43 -0
  322. package/packages/core/src/priority-queue/__tests__/backoff.test.ts +23 -0
  323. package/packages/core/src/priority-queue/__tests__/index.test.ts +158 -0
  324. package/packages/core/src/priority-queue/backoff.ts +24 -0
  325. package/packages/core/src/priority-queue/index.ts +103 -0
  326. package/packages/core/src/queue/__tests__/event-queue.test.ts +678 -0
  327. package/packages/core/src/queue/__tests__/extension-flushing.test.ts +416 -0
  328. package/packages/core/src/queue/delivery.ts +73 -0
  329. package/packages/core/src/queue/event-queue.ts +318 -0
  330. package/packages/core/src/stats/__tests__/index.test.ts +103 -0
  331. package/packages/core/src/stats/index.ts +88 -0
  332. package/packages/core/src/task/__tests__/task-group.test.ts +26 -0
  333. package/packages/core/src/task/task-group.ts +31 -0
  334. package/packages/core/src/user/index.ts +7 -0
  335. package/packages/core/src/utils/__tests__/group-by.test.ts +96 -0
  336. package/packages/core/src/utils/__tests__/is-plain-object.test.ts +27 -0
  337. package/packages/core/src/utils/__tests__/is-thenable.test.ts +39 -0
  338. package/packages/core/src/utils/bind-all.ts +19 -0
  339. package/packages/core/src/utils/get-global.ts +17 -0
  340. package/packages/core/src/utils/group-by.ts +30 -0
  341. package/packages/core/src/utils/has-properties.ts +7 -0
  342. package/packages/core/src/utils/is-plain-object.ts +26 -0
  343. package/packages/core/src/utils/is-thenable.ts +9 -0
  344. package/packages/core/src/utils/p-while.ts +12 -0
  345. package/packages/core/src/utils/pick.ts +8 -0
  346. package/packages/core/src/utils/ts-helpers.ts +13 -0
  347. package/packages/core/src/validation/__tests__/assertions.test.ts +155 -0
  348. package/packages/core/src/validation/assertions.ts +72 -0
  349. package/packages/core/src/validation/errors.ts +8 -0
  350. package/packages/core/src/validation/helpers.ts +23 -0
  351. package/packages/core/test-helpers/index.ts +2 -0
  352. package/packages/core/test-helpers/test-ctx.ts +7 -0
  353. package/packages/core/test-helpers/test-event-queue.ts +7 -0
  354. package/packages/core/tsconfig.build.json +9 -0
  355. package/packages/core/tsconfig.json +11 -0
  356. package/packages/node/.eslintrc.js +7 -0
  357. package/packages/node/.lintstagedrc.js +1 -0
  358. package/packages/node/LICENSE +45 -0
  359. package/packages/node/README.md +138 -0
  360. package/packages/node/jest.config.js +5 -0
  361. package/packages/node/jest.setup.js +4 -0
  362. package/packages/node/package.json +50 -0
  363. package/packages/node/scripts/version.sh +11 -0
  364. package/packages/node/src/__tests__/callback.test.ts +47 -0
  365. package/packages/node/src/__tests__/disable.integration.test.ts +42 -0
  366. package/packages/node/src/__tests__/emitter.integration.test.ts +45 -0
  367. package/packages/node/src/__tests__/graceful-shutdown-integration.test.ts +244 -0
  368. package/packages/node/src/__tests__/http-client.integration.test.ts +69 -0
  369. package/packages/node/src/__tests__/http-integration.test.ts +362 -0
  370. package/packages/node/src/__tests__/integration.test.ts +357 -0
  371. package/packages/node/src/__tests__/plugins.test.ts +16 -0
  372. package/packages/node/src/__tests__/settings.test.ts +9 -0
  373. package/packages/node/src/__tests__/test-helpers/assert-shape/http-request-event.ts +13 -0
  374. package/packages/node/src/__tests__/test-helpers/assert-shape/index.ts +2 -0
  375. package/packages/node/src/__tests__/test-helpers/assert-shape/segment-http-api.ts +43 -0
  376. package/packages/node/src/__tests__/test-helpers/create-test-analytics.ts +42 -0
  377. package/packages/node/src/__tests__/test-helpers/factories.ts +17 -0
  378. package/packages/node/src/__tests__/test-helpers/is-valid-date.ts +6 -0
  379. package/packages/node/src/__tests__/test-helpers/resolve-ctx.ts +19 -0
  380. package/packages/node/src/__tests__/test-helpers/resolve-emitter.ts +11 -0
  381. package/packages/node/src/__tests__/test-helpers/sleep.ts +3 -0
  382. package/packages/node/src/__tests__/test-helpers/test-plugin.ts +16 -0
  383. package/packages/node/src/__tests__/typedef-tests.ts +120 -0
  384. package/packages/node/src/app/analytics-node.ts +299 -0
  385. package/packages/node/src/app/context.ts +11 -0
  386. package/packages/node/src/app/dispatch-emit.ts +42 -0
  387. package/packages/node/src/app/emitter.ts +23 -0
  388. package/packages/node/src/app/event-factory.ts +20 -0
  389. package/packages/node/src/app/event-queue.ts +23 -0
  390. package/packages/node/src/app/settings.ts +49 -0
  391. package/packages/node/src/app/types/index.ts +3 -0
  392. package/packages/node/src/app/types/params.ts +76 -0
  393. package/packages/node/src/app/types/plugin.ts +5 -0
  394. package/packages/node/src/app/types/segment-event.ts +7 -0
  395. package/packages/node/src/generated/version.ts +2 -0
  396. package/packages/node/src/index.ts +26 -0
  397. package/packages/node/src/lib/__tests__/abort.test.ts +54 -0
  398. package/packages/node/src/lib/__tests__/create-url.test.ts +35 -0
  399. package/packages/node/src/lib/__tests__/env.test.ts +52 -0
  400. package/packages/node/src/lib/__tests__/get-message-id.test.ts +21 -0
  401. package/packages/node/src/lib/abort.ts +77 -0
  402. package/packages/node/src/lib/base-64-encode.ts +8 -0
  403. package/packages/node/src/lib/create-url.ts +11 -0
  404. package/packages/node/src/lib/env.ts +45 -0
  405. package/packages/node/src/lib/extract-promise-parts.ts +21 -0
  406. package/packages/node/src/lib/fetch.ts +16 -0
  407. package/packages/node/src/lib/get-message-id.ts +10 -0
  408. package/packages/node/src/lib/http-client.ts +95 -0
  409. package/packages/node/src/lib/uuid.ts +1 -0
  410. package/packages/node/src/plugins/segmentio/__tests__/methods.test.ts +223 -0
  411. package/packages/node/src/plugins/segmentio/__tests__/publisher.test.ts +411 -0
  412. package/packages/node/src/plugins/segmentio/context-batch.ts +71 -0
  413. package/packages/node/src/plugins/segmentio/index.ts +66 -0
  414. package/packages/node/src/plugins/segmentio/publisher.ts +265 -0
  415. package/packages/node/tsconfig.build.json +9 -0
  416. package/packages/node/tsconfig.json +10 -0
  417. package/packages/test-helpers/.eslintrc.js +7 -0
  418. package/packages/test-helpers/.lintstagedrc.js +1 -0
  419. package/packages/test-helpers/jest.config.js +3 -0
  420. package/packages/test-helpers/package.json +26 -0
  421. package/packages/test-helpers/src/analytics/cdn-settings-builder.ts +79 -0
  422. package/packages/test-helpers/src/analytics/index.ts +1 -0
  423. package/packages/test-helpers/src/index.ts +2 -0
  424. package/packages/test-helpers/src/utils/index.ts +1 -0
  425. package/packages/test-helpers/src/utils/sleep.ts +4 -0
  426. package/packages/test-helpers/tsconfig.build.json +9 -0
  427. package/packages/test-helpers/tsconfig.json +11 -0
  428. package/tsconfig.json +21 -0
  429. package/turbo.json +39 -0
  430. package/typings/get-monorepo-packages.d.ts +9 -0
  431. package/typings/spawn.d.ts +10 -0
@@ -0,0 +1,1213 @@
1
+ /* eslint-disable @typescript-eslint/no-floating-promises */
2
+ import { cdnSettingsKitchenSink } from '../../test-helpers/fixtures/cdn-settings'
3
+ import { createMockFetchImplementation } from '../../test-helpers/fixtures/create-fetch-method'
4
+ import { Context } from '../../core/context'
5
+ import { Plugin } from '../../core/plugin'
6
+ import { JSDOM } from 'jsdom'
7
+ import { Analytics, InitOptions } from '../../core/analytics'
8
+ import { LegacyDestination } from '../../plugins/ajs-destination'
9
+ import { PersistedPriorityQueue } from '../../lib/priority-queue/persisted'
10
+ // @ts-ignore loadLegacySettings mocked dependency is accused as unused
11
+ import { HtEventsBrowser, loadLegacySettings } from '..'
12
+ // @ts-ignore isOffline mocked dependency is accused as unused
13
+ import { isOffline } from '../../core/connection'
14
+ import * as HightouchPlugin from '../../plugins/hightouchio'
15
+ import jar from 'js-cookie'
16
+ import { PriorityQueue } from '../../lib/priority-queue'
17
+ import { getCDN, setGlobalCDNUrl } from '../../lib/parse-cdn'
18
+ import { clearAjsBrowserStorage } from '../../test-helpers/browser-storage'
19
+ import { parseFetchCall } from '../../test-helpers/fetch-parse'
20
+ import { ActionDestination } from '../../plugins/remote-loader'
21
+ import { UADataValues } from '../../lib/client-hints/interfaces'
22
+ import {
23
+ highEntropyTestData,
24
+ lowEntropyTestData,
25
+ } from '../../test-helpers/fixtures/client-hints'
26
+ import { getGlobalAnalytics } from '../..'
27
+
28
+ let fetchCalls: ReturnType<typeof parseFetchCall>[] = []
29
+
30
+ jest.mock('unfetch', () => {
31
+ return {
32
+ __esModule: true,
33
+ default: (url: RequestInfo, body?: RequestInit) => {
34
+ const call = parseFetchCall([url, body])
35
+ fetchCalls.push(call)
36
+ return createMockFetchImplementation(cdnSettingsKitchenSink)(url, body)
37
+ },
38
+ }
39
+ })
40
+
41
+ const sleep = (time: number): Promise<void> =>
42
+ new Promise((resolve) => {
43
+ setTimeout(resolve, time)
44
+ })
45
+
46
+ const xt: Plugin = {
47
+ name: 'Test Plugin',
48
+ type: 'utility',
49
+ version: '1.0',
50
+
51
+ load(_ctx: Context): Promise<void> {
52
+ return Promise.resolve()
53
+ },
54
+
55
+ isLoaded(): boolean {
56
+ return true
57
+ },
58
+
59
+ track: async (ctx) => ctx,
60
+ identify: async (ctx) => ctx,
61
+ page: async (ctx) => ctx,
62
+ group: async (ctx) => ctx,
63
+ alias: async (ctx) => ctx,
64
+ }
65
+
66
+ const amplitude: Plugin = {
67
+ ...xt,
68
+ name: 'Amplitude',
69
+ type: 'destination',
70
+ }
71
+
72
+ const googleAnalytics: Plugin = {
73
+ ...xt,
74
+ name: 'Google Analytics',
75
+ type: 'destination',
76
+ }
77
+
78
+ const enrichBilling: Plugin = {
79
+ ...xt,
80
+ name: 'Billing Enrichment',
81
+ type: 'enrichment',
82
+
83
+ track: async (ctx) => {
84
+ ctx.event.properties = {
85
+ ...ctx.event.properties,
86
+ billingPlan: 'free-99',
87
+ }
88
+ return ctx
89
+ },
90
+ }
91
+
92
+ const writeKey = 'foo'
93
+ const amplitudeWriteKey = 'bar'
94
+
95
+ beforeEach(() => {
96
+ setGlobalCDNUrl(undefined as any)
97
+ })
98
+
99
+ describe('Initialization', () => {
100
+ beforeEach(async () => {
101
+ fetchCalls = []
102
+ jest.resetAllMocks()
103
+ jest.resetModules()
104
+ })
105
+
106
+ it('loads plugins', async () => {
107
+ await HtEventsBrowser.load({
108
+ writeKey,
109
+ plugins: [xt],
110
+ })
111
+
112
+ expect(xt.isLoaded()).toBe(true)
113
+ })
114
+
115
+ it('loads async plugins', async () => {
116
+ let pluginLoaded = false
117
+ const onLoad = jest.fn(() => {
118
+ pluginLoaded = true
119
+ })
120
+
121
+ const lazyPlugin: Plugin = {
122
+ name: 'Test 2',
123
+ type: 'utility',
124
+ version: '1.0',
125
+
126
+ load: async (_ctx) => {
127
+ setTimeout(onLoad, 300)
128
+ },
129
+ isLoaded: () => {
130
+ return pluginLoaded
131
+ },
132
+ }
133
+
134
+ jest.spyOn(lazyPlugin, 'load')
135
+ await HtEventsBrowser.load({ writeKey, plugins: [lazyPlugin] })
136
+
137
+ expect(lazyPlugin.load).toHaveBeenCalled()
138
+ expect(onLoad).not.toHaveBeenCalled()
139
+ expect(pluginLoaded).toBe(false)
140
+
141
+ await sleep(300)
142
+
143
+ expect(onLoad).toHaveBeenCalled()
144
+ expect(pluginLoaded).toBe(true)
145
+ })
146
+
147
+ it('ready method is called only when all plugins with ready have declared themselves as ready', async () => {
148
+ const ready = jest.fn()
149
+
150
+ const lazyPlugin1: Plugin = {
151
+ name: 'Test 2',
152
+ type: 'destination',
153
+ version: '1.0',
154
+
155
+ load: async (_ctx) => {},
156
+ ready: async () => {
157
+ return new Promise((resolve) => setTimeout(resolve, 300))
158
+ },
159
+ isLoaded: () => true,
160
+ }
161
+
162
+ const lazyPlugin2: Plugin = {
163
+ name: 'Test 2',
164
+ type: 'destination',
165
+ version: '1.0',
166
+
167
+ load: async (_ctx) => {},
168
+ ready: async () => {
169
+ return new Promise((resolve) => setTimeout(resolve, 100))
170
+ },
171
+ isLoaded: () => true,
172
+ }
173
+
174
+ jest.spyOn(lazyPlugin1, 'load')
175
+ jest.spyOn(lazyPlugin2, 'load')
176
+ const [analytics] = await HtEventsBrowser.load({
177
+ writeKey,
178
+ plugins: [lazyPlugin1, lazyPlugin2, xt],
179
+ })
180
+
181
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
182
+ analytics.ready(ready)
183
+ expect(lazyPlugin1.load).toHaveBeenCalled()
184
+ expect(lazyPlugin2.load).toHaveBeenCalled()
185
+ expect(ready).not.toHaveBeenCalled()
186
+
187
+ await sleep(100)
188
+ expect(ready).not.toHaveBeenCalled()
189
+
190
+ await sleep(200)
191
+ expect(ready).toHaveBeenCalled()
192
+ })
193
+
194
+ describe('cdn', () => {
195
+ it('should get the correct CDN in plugins if the CDN overridden', async () => {
196
+ const overriddenCDNUrl = 'http://cdn.hightouch-events.com' // http instead of https
197
+ await HtEventsBrowser.load({
198
+ cdnURL: overriddenCDNUrl,
199
+ writeKey,
200
+ plugins: [
201
+ {
202
+ ...xt,
203
+ load: async () => {
204
+ expect(getGlobalAnalytics()).toBeUndefined()
205
+ expect(getCDN()).toContain(overriddenCDNUrl)
206
+ },
207
+ },
208
+ ],
209
+ })
210
+
211
+ // cdn settings dont get called anymore
212
+ // expect(fetchCalls[0].url).toContain(overriddenCDNUrl)
213
+ // expect.assertions(3)
214
+ })
215
+ })
216
+
217
+ describe('globalAnalyticsKey', () => {
218
+ const overrideKey = 'myKey'
219
+ const buffer = {
220
+ foo: 'bar',
221
+ }
222
+
223
+ beforeEach(() => {
224
+ ;(window as any)[overrideKey] = buffer
225
+ })
226
+ afterEach(() => {
227
+ delete (window as any)[overrideKey]
228
+ })
229
+ it('should default to window.htevents', async () => {
230
+ const defaultObj = { original: 'default' }
231
+ ;(window as any)['htevents'] = defaultObj
232
+
233
+ await HtEventsBrowser.load({
234
+ writeKey,
235
+ plugins: [
236
+ {
237
+ ...xt,
238
+ load: async () => {
239
+ expect(getGlobalAnalytics()).toBe(defaultObj)
240
+ },
241
+ },
242
+ ],
243
+ })
244
+ expect.assertions(1)
245
+ })
246
+
247
+ it('should set the global window key for the analytics buffer with the setting option', async () => {
248
+ await HtEventsBrowser.load(
249
+ {
250
+ writeKey,
251
+ plugins: [
252
+ {
253
+ ...xt,
254
+ load: async () => {
255
+ expect(getGlobalAnalytics()).toBe(buffer)
256
+ },
257
+ },
258
+ ],
259
+ },
260
+ {
261
+ globalAnalyticsKey: overrideKey,
262
+ }
263
+ )
264
+ expect.assertions(1)
265
+ })
266
+ })
267
+
268
+ describe('Load options', () => {
269
+ it('gets high entropy client hints if set', async () => {
270
+ ;(window.navigator as any).userAgentData = {
271
+ ...lowEntropyTestData,
272
+ getHighEntropyValues: jest
273
+ .fn()
274
+ .mockImplementation((hints: string[]): Promise<UADataValues> => {
275
+ let result = {}
276
+ Object.entries(highEntropyTestData).forEach(([k, v]) => {
277
+ if (hints.includes(k)) {
278
+ result = {
279
+ ...result,
280
+ [k]: v,
281
+ }
282
+ }
283
+ })
284
+ return Promise.resolve({
285
+ ...lowEntropyTestData,
286
+ ...result,
287
+ })
288
+ }),
289
+ toJSON: jest.fn(() => lowEntropyTestData),
290
+ }
291
+
292
+ const [ajs] = await HtEventsBrowser.load(
293
+ { writeKey },
294
+ { highEntropyValuesClientHints: ['architecture'] }
295
+ )
296
+
297
+ const evt = await ajs.track('foo')
298
+ expect(evt.event.context?.userAgentData).toEqual({
299
+ ...lowEntropyTestData,
300
+ architecture: 'x86',
301
+ })
302
+ })
303
+ it('calls page if initialpageview is set', async () => {
304
+ jest.mock('../../core/analytics')
305
+ const mockPage = jest.fn().mockImplementation(() => Promise.resolve())
306
+ Analytics.prototype.page = mockPage
307
+
308
+ await HtEventsBrowser.load({ writeKey }, { initialPageview: true })
309
+
310
+ expect(mockPage).toHaveBeenCalled()
311
+ })
312
+
313
+ it('does not call page if initialpageview is not set', async () => {
314
+ jest.mock('../../core/analytics')
315
+ const mockPage = jest.fn()
316
+ Analytics.prototype.page = mockPage
317
+ await HtEventsBrowser.load({ writeKey }, { initialPageview: false })
318
+ expect(mockPage).not.toHaveBeenCalled()
319
+ })
320
+
321
+ it('does not use a persisted queue when disableClientPersistence is true', async () => {
322
+ const [ajs] = await HtEventsBrowser.load(
323
+ {
324
+ writeKey,
325
+ },
326
+ {
327
+ disableClientPersistence: true,
328
+ }
329
+ )
330
+
331
+ expect(ajs.queue.queue instanceof PriorityQueue).toBe(true)
332
+ expect(ajs.queue.queue instanceof PersistedPriorityQueue).toBe(false)
333
+ })
334
+
335
+ it('uses a persisted queue by default', async () => {
336
+ const [ajs] = await HtEventsBrowser.load({
337
+ writeKey,
338
+ })
339
+
340
+ expect(ajs.queue.queue instanceof PersistedPriorityQueue).toBe(true)
341
+ })
342
+
343
+ it('disables identity persistance when disableClientPersistence is true', async () => {
344
+ const [ajs] = await HtEventsBrowser.load(
345
+ {
346
+ writeKey,
347
+ },
348
+ {
349
+ disableClientPersistence: true,
350
+ }
351
+ )
352
+
353
+ expect(ajs.user().options.persist).toBe(false)
354
+ expect(ajs.group().options.persist).toBe(false)
355
+ })
356
+
357
+ it('doesnt fetch remote source settings by default', async () => {
358
+ await HtEventsBrowser.load({
359
+ writeKey,
360
+ })
361
+
362
+ expect(fetchCalls.length).toBe(0)
363
+ // expect(fetchCalls[0].url).toMatch(/\/settings$/)
364
+ })
365
+
366
+ it('does not fetch source settings if cdnSettings is set', async () => {
367
+ await HtEventsBrowser.load({
368
+ writeKey,
369
+ cdnSettings: { integrations: {} },
370
+ })
371
+
372
+ expect(fetchCalls.length).toBe(0)
373
+ })
374
+ })
375
+
376
+ describe('options.integrations permutations', () => {
377
+ const settings = { writeKey }
378
+
379
+ it('does not load Hightouch.io if integrations.All is false and Hightouch.io is not listed', async () => {
380
+ const options: { integrations: { [key: string]: boolean } } = {
381
+ integrations: { All: false },
382
+ }
383
+ const analyticsResponse = await HtEventsBrowser.load(settings, options)
384
+
385
+ const hightouchio = analyticsResponse[0].queue.plugins.find(
386
+ (p) => p.name === 'Hightouch.io'
387
+ )
388
+
389
+ expect(hightouchio).toBeUndefined()
390
+ })
391
+
392
+ it('does not load Hightouch.io if its set to false', async () => {
393
+ const options: { integrations?: { [key: string]: boolean } } = {
394
+ integrations: { 'Hightouch.io': false },
395
+ }
396
+ const analyticsResponse = await HtEventsBrowser.load(settings, options)
397
+
398
+ const hightouchio = analyticsResponse[0].queue.plugins.find(
399
+ (p) => p.name === 'Hightouch.io'
400
+ )
401
+
402
+ expect(hightouchio).toBeUndefined()
403
+ })
404
+
405
+ it('loads Hightouch.io if integrations.All is false and Hightouch.io is listed', async () => {
406
+ const options: { integrations: { [key: string]: boolean } } = {
407
+ integrations: { All: false, 'Hightouch.io': true },
408
+ }
409
+ const analyticsResponse = await HtEventsBrowser.load(settings, options)
410
+
411
+ const hightouchio = analyticsResponse[0].queue.plugins.find(
412
+ (p) => p.name === 'Hightouch.io'
413
+ )
414
+
415
+ expect(hightouchio).toBeDefined()
416
+ })
417
+
418
+ it('loads Hightouch.io if integrations.All is undefined', async () => {
419
+ const options: { integrations: { [key: string]: boolean } } = {
420
+ integrations: { 'Hightouch.io': true },
421
+ }
422
+ const analyticsResponse = await HtEventsBrowser.load(settings, options)
423
+
424
+ const hightouchio = analyticsResponse[0].queue.plugins.find(
425
+ (p) => p.name === 'Hightouch.io'
426
+ )
427
+
428
+ expect(hightouchio).toBeDefined()
429
+ })
430
+
431
+ it('loads Hightouch.io if integrations is undefined', async () => {
432
+ const options: { integrations?: { [key: string]: boolean } } = {
433
+ integrations: undefined,
434
+ }
435
+ const analyticsResponse = await HtEventsBrowser.load(settings, options)
436
+
437
+ const hightouchio = analyticsResponse[0].queue.plugins.find(
438
+ (p) => p.name === 'Hightouch.io'
439
+ )
440
+
441
+ expect(hightouchio).toBeDefined()
442
+ })
443
+
444
+ it('loads selected plugins when Hightouch.io is false', async () => {
445
+ const options: { integrations?: { [key: string]: boolean } } = {
446
+ integrations: {
447
+ 'Test Plugin': true,
448
+ 'Hightouch.io': false,
449
+ },
450
+ }
451
+ const analyticsResponse = await HtEventsBrowser.load(
452
+ { ...settings, plugins: [xt] },
453
+ options
454
+ )
455
+
456
+ const plugin = analyticsResponse[0].queue.plugins.find(
457
+ (p) => p.name === 'Test Plugin'
458
+ )
459
+
460
+ const hightouchio = analyticsResponse[0].queue.plugins.find(
461
+ (p) => p.name === 'Hightouch.io'
462
+ )
463
+
464
+ expect(plugin).toBeDefined()
465
+ expect(hightouchio).toBeUndefined()
466
+ })
467
+
468
+ it('loads selected plugins when Hightouch.io and All are false', async () => {
469
+ const options: { integrations?: { [key: string]: boolean } } = {
470
+ integrations: {
471
+ All: false,
472
+ 'Test Plugin': true,
473
+ 'Hightouch.io': false,
474
+ },
475
+ }
476
+ const analyticsResponse = await HtEventsBrowser.load(
477
+ { ...settings, plugins: [xt] },
478
+ options
479
+ )
480
+
481
+ const plugin = analyticsResponse[0].queue.plugins.find(
482
+ (p) => p.name === 'Test Plugin'
483
+ )
484
+
485
+ const hightouchio = analyticsResponse[0].queue.plugins.find(
486
+ (p) => p.name === 'Hightouch.io'
487
+ )
488
+
489
+ expect(plugin).toBeDefined()
490
+ expect(hightouchio).toBeUndefined()
491
+ })
492
+ })
493
+ })
494
+
495
+ describe('Dispatch', () => {
496
+ it('dispatches events to destinations', async () => {
497
+ const [ajs] = await HtEventsBrowser.load({
498
+ writeKey,
499
+ plugins: [amplitude, googleAnalytics],
500
+ })
501
+
502
+ const hightouchio = ajs.queue.plugins.find((p) => p.name === 'Hightouch.io')
503
+ expect(hightouchio).toBeDefined()
504
+
505
+ const ampSpy = jest.spyOn(amplitude, 'track')
506
+ const gaSpy = jest.spyOn(googleAnalytics, 'track')
507
+ const hightouchSpy = jest.spyOn(hightouchio!, 'track')
508
+
509
+ const boo = await ajs.track('Boo!', {
510
+ total: 25,
511
+ userId: '👻',
512
+ })
513
+
514
+ expect(ampSpy).toHaveBeenCalledWith(boo)
515
+ expect(gaSpy).toHaveBeenCalledWith(boo)
516
+ expect(hightouchSpy).toHaveBeenCalledWith(boo)
517
+ })
518
+
519
+ it('does not dispatch events to destinations on deny list', async () => {
520
+ const [ajs] = await HtEventsBrowser.load({
521
+ writeKey,
522
+ plugins: [amplitude, googleAnalytics],
523
+ })
524
+
525
+ const hightouchio = ajs.queue.plugins.find((p) => p.name === 'Hightouch.io')
526
+ expect(hightouchio).toBeDefined()
527
+
528
+ const ampSpy = jest.spyOn(amplitude, 'track')
529
+ const gaSpy = jest.spyOn(googleAnalytics, 'track')
530
+ const hightouchSpy = jest.spyOn(hightouchio!, 'track')
531
+
532
+ const boo = await ajs.track(
533
+ 'Boo!',
534
+ {
535
+ total: 25,
536
+ userId: '👻',
537
+ },
538
+ {
539
+ integrations: {
540
+ Amplitude: false,
541
+ 'Hightouch.io': false,
542
+ },
543
+ }
544
+ )
545
+
546
+ expect(gaSpy).toHaveBeenCalledWith(boo)
547
+ expect(ampSpy).not.toHaveBeenCalled()
548
+ expect(hightouchSpy).not.toHaveBeenCalled()
549
+ })
550
+
551
+ it('does dispatch events to Hightouch.io when All is false', async () => {
552
+ const [ajs] = await HtEventsBrowser.load({
553
+ writeKey,
554
+ plugins: [amplitude, googleAnalytics],
555
+ })
556
+
557
+ const hightouchio = ajs.queue.plugins.find((p) => p.name === 'Hightouch.io')
558
+ expect(hightouchio).toBeDefined()
559
+
560
+ const ampSpy = jest.spyOn(amplitude, 'track')
561
+ const gaSpy = jest.spyOn(googleAnalytics, 'track')
562
+ const hightouchSpy = jest.spyOn(hightouchio!, 'track')
563
+
564
+ const boo = await ajs.track(
565
+ 'Boo!',
566
+ {
567
+ total: 25,
568
+ userId: '👻',
569
+ },
570
+ {
571
+ integrations: {
572
+ All: false,
573
+ },
574
+ }
575
+ )
576
+
577
+ expect(gaSpy).not.toHaveBeenCalled()
578
+ expect(ampSpy).not.toHaveBeenCalled()
579
+ expect(hightouchSpy).toHaveBeenCalledWith(boo)
580
+ })
581
+
582
+ it('enriches events before dispatching', async () => {
583
+ const [ajs] = await HtEventsBrowser.load({
584
+ writeKey,
585
+ plugins: [enrichBilling, amplitude, googleAnalytics],
586
+ })
587
+
588
+ const boo = await ajs.track('Boo!', {
589
+ total: 25,
590
+ })
591
+
592
+ expect(boo.event.properties).toMatchInlineSnapshot(`
593
+ Object {
594
+ "billingPlan": "free-99",
595
+ "total": 25,
596
+ }
597
+ `)
598
+ })
599
+
600
+ it('collects metrics for every event', async () => {
601
+ const [ajs] = await HtEventsBrowser.load({
602
+ writeKey,
603
+ plugins: [amplitude],
604
+ })
605
+
606
+ const delivered = await ajs.track('Fruit Basket', {
607
+ items: ['🍌', '🍇', '🍎'],
608
+ userId: 'Healthy person',
609
+ })
610
+
611
+ const metrics = delivered.stats.metrics
612
+
613
+ expect(metrics.map((m) => m.metric)).toMatchInlineSnapshot(`
614
+ Array [
615
+ "message_dispatched",
616
+ "plugin_time",
617
+ "plugin_time",
618
+ "plugin_time",
619
+ "message_delivered",
620
+ "plugin_time",
621
+ "delivered",
622
+ ]
623
+ `)
624
+ })
625
+ })
626
+
627
+ describe('Group', () => {
628
+ it('manages Group state', async () => {
629
+ const [analytics] = await HtEventsBrowser.load({
630
+ writeKey,
631
+ })
632
+
633
+ const group = analytics.group()
634
+
635
+ const ctx = await analytics.group('coolKids', {
636
+ coolKids: true,
637
+ })
638
+
639
+ expect(ctx.event.groupId).toEqual('coolKids')
640
+ expect(ctx.event.traits).toEqual({ coolKids: true })
641
+
642
+ expect(group.id()).toEqual('coolKids')
643
+ expect(group.traits()).toEqual({ coolKids: true })
644
+ })
645
+ })
646
+
647
+ describe('Alias', () => {
648
+ it('generates alias events', async () => {
649
+ const [analytics] = await HtEventsBrowser.load({
650
+ writeKey,
651
+ plugins: [amplitude],
652
+ })
653
+
654
+ jest.spyOn(amplitude, 'alias')
655
+
656
+ const ctx = await analytics.alias('netto farah', 'netto')
657
+
658
+ expect(ctx.event.userId).toEqual('netto farah')
659
+ expect(ctx.event.previousId).toEqual('netto')
660
+
661
+ expect(amplitude.alias).toHaveBeenCalled()
662
+ })
663
+
664
+ it('falls back to userID in cookies if no id passed', async () => {
665
+ jar.set('htjs_user_id', 'dan')
666
+ const [analytics] = await HtEventsBrowser.load({
667
+ writeKey,
668
+ plugins: [amplitude],
669
+ })
670
+
671
+ jest.spyOn(amplitude, 'alias')
672
+
673
+ // @ts-ignore ajs 1.0 parity - allows empty alias calls
674
+ const ctx = await analytics.alias()
675
+
676
+ expect(ctx.event.userId).toEqual('dan')
677
+ expect(amplitude.alias).toHaveBeenCalled()
678
+ })
679
+ })
680
+
681
+ describe('pageview', () => {
682
+ it('makes a page call with the given url', async () => {
683
+ console.warn = (): void => {}
684
+ const analytics = new Analytics({ writeKey: writeKey })
685
+ const mockPage = jest.spyOn(analytics, 'page')
686
+ await analytics.pageview('www.foo.com')
687
+
688
+ expect(mockPage).toHaveBeenCalledWith({ path: 'www.foo.com' })
689
+ })
690
+ })
691
+
692
+ describe('setAnonymousId', () => {
693
+ beforeEach(() => {
694
+ clearAjsBrowserStorage()
695
+ })
696
+
697
+ it('calling setAnonymousId will set a new anonymousId and returns it', async () => {
698
+ const [analytics] = await HtEventsBrowser.load({
699
+ writeKey,
700
+ plugins: [amplitude],
701
+ })
702
+
703
+ const currentAnonymousId = analytics.user().anonymousId()
704
+ expect(currentAnonymousId).toBeDefined()
705
+ expect(currentAnonymousId).toHaveLength(36)
706
+
707
+ const newAnonymousId = analytics.setAnonymousId('🦹‍♀️')
708
+
709
+ expect(analytics.user().anonymousId()).toEqual('🦹‍♀️')
710
+ expect(newAnonymousId).toEqual('🦹‍♀️')
711
+ })
712
+ })
713
+
714
+ describe('addSourceMiddleware', () => {
715
+ it('supports registering source middlewares', async () => {
716
+ const [analytics] = await HtEventsBrowser.load({
717
+ writeKey,
718
+ })
719
+
720
+ await analytics
721
+ .addSourceMiddleware(({ next, payload }) => {
722
+ payload.obj.context = {
723
+ hello: 'from the other side',
724
+ }
725
+ next(payload)
726
+ })
727
+ .catch((err) => {
728
+ throw err
729
+ })
730
+
731
+ const ctx = await analytics.track('Hello!')
732
+
733
+ expect(ctx.event.context).toMatchObject({
734
+ hello: 'from the other side',
735
+ })
736
+ })
737
+ })
738
+
739
+ // We do not support remote integrations
740
+ describe.skip('addDestinationMiddleware', () => {
741
+ beforeEach(async () => {
742
+ jest.restoreAllMocks()
743
+ jest.resetAllMocks()
744
+
745
+ const html = `
746
+ <!DOCTYPE html>
747
+ <head>
748
+ <script>'hi'</script>
749
+ </head>
750
+ <body>
751
+ </body>
752
+ </html>
753
+ `.trim()
754
+
755
+ const jsd = new JSDOM(html, {
756
+ runScripts: 'dangerously',
757
+ resources: 'usable',
758
+ url: 'https://localhost',
759
+ })
760
+
761
+ const windowSpy = jest.spyOn(global, 'window', 'get')
762
+ windowSpy.mockImplementation(
763
+ () => jsd.window as unknown as Window & typeof globalThis
764
+ )
765
+ })
766
+
767
+ it('supports registering destination middlewares', async () => {
768
+ const [analytics] = await HtEventsBrowser.load({
769
+ writeKey,
770
+ })
771
+
772
+ const amplitude = new LegacyDestination(
773
+ 'amplitude',
774
+ 'latest',
775
+ writeKey,
776
+ {
777
+ apiKey: amplitudeWriteKey,
778
+ },
779
+ {}
780
+ )
781
+
782
+ await analytics.register(amplitude)
783
+ await amplitude.ready()
784
+
785
+ analytics
786
+ .addDestinationMiddleware('amplitude', ({ next, payload }) => {
787
+ payload.obj.properties!.hello = 'from the other side'
788
+ next(payload)
789
+ })
790
+ .catch((err) => {
791
+ throw err
792
+ })
793
+
794
+ const integrationMock = jest.spyOn(amplitude.integration!, 'track')
795
+ const ctx = await analytics.track('Hello!')
796
+
797
+ // does not modify the event
798
+ expect(ctx.event.properties).not.toEqual({
799
+ hello: 'from the other side',
800
+ })
801
+
802
+ const calledWith = integrationMock.mock.calls[0][0].properties()
803
+
804
+ // only impacted this destination
805
+ expect(calledWith).toEqual({
806
+ ...ctx.event.properties,
807
+ hello: 'from the other side',
808
+ })
809
+ })
810
+
811
+ it('supports registering action destination middlewares', async () => {
812
+ const testPlugin: Plugin = {
813
+ name: 'test',
814
+ type: 'destination',
815
+ version: '0.1.0',
816
+ load: () => Promise.resolve(),
817
+ isLoaded: () => true,
818
+ }
819
+
820
+ const [analytics] = await HtEventsBrowser.load({
821
+ writeKey,
822
+ })
823
+
824
+ const fullstory = new ActionDestination('fullstory', testPlugin)
825
+
826
+ await analytics.register(fullstory)
827
+ await fullstory.ready()
828
+
829
+ analytics
830
+ .addDestinationMiddleware('fullstory', ({ next, payload }) =>
831
+ next(payload)
832
+ )
833
+ .catch((err) => {
834
+ throw err
835
+ })
836
+
837
+ expect(analytics.queue.plugins).toContain(fullstory)
838
+ })
839
+ })
840
+
841
+ describe('use', () => {
842
+ it('registers a legacyPlugin', async () => {
843
+ const [analytics] = await HtEventsBrowser.load({
844
+ writeKey,
845
+ })
846
+
847
+ const legacyPlugin = jest.fn()
848
+ analytics.use(legacyPlugin)
849
+
850
+ expect(legacyPlugin).toHaveBeenCalledWith(analytics)
851
+ })
852
+ })
853
+
854
+ describe('timeout', () => {
855
+ it('has a default timeout value', async () => {
856
+ const [analytics] = await HtEventsBrowser.load({
857
+ writeKey,
858
+ })
859
+ //@ts-ignore
860
+ expect(analytics.settings.timeout).toEqual(300)
861
+ })
862
+
863
+ it('can set a timeout value', async () => {
864
+ const [analytics] = await HtEventsBrowser.load({
865
+ writeKey,
866
+ })
867
+ analytics.timeout(50)
868
+ //@ts-ignore
869
+ expect(analytics.settings.timeout).toEqual(50)
870
+ })
871
+ })
872
+
873
+ // We do not support remote integrations
874
+ describe.skip('deregister', () => {
875
+ beforeEach(async () => {
876
+ jest.restoreAllMocks()
877
+ jest.resetAllMocks()
878
+
879
+ const html = `
880
+ <!DOCTYPE html>
881
+ <head>
882
+ <script>'hi'</script>
883
+ </head>
884
+ <body>
885
+ </body>
886
+ </html>
887
+ `.trim()
888
+
889
+ const jsd = new JSDOM(html, {
890
+ runScripts: 'dangerously',
891
+ resources: 'usable',
892
+ url: 'https://localhost',
893
+ })
894
+
895
+ const windowSpy = jest.spyOn(global, 'window', 'get')
896
+ windowSpy.mockImplementation(
897
+ () => jsd.window as unknown as Window & typeof globalThis
898
+ )
899
+ })
900
+
901
+ it('deregisters a plugin given its name', async () => {
902
+ const unload = jest.fn((): Promise<unknown> => {
903
+ return Promise.resolve()
904
+ })
905
+ xt.unload = unload
906
+
907
+ const [analytics] = await HtEventsBrowser.load({
908
+ writeKey,
909
+ plugins: [xt],
910
+ })
911
+
912
+ await analytics.deregister('Test Plugin')
913
+ expect(xt.unload).toHaveBeenCalled()
914
+ })
915
+
916
+ it('cleans up the DOM when deregistering a legacy integration', async () => {
917
+ const amplitude = new LegacyDestination(
918
+ 'amplitude',
919
+ 'latest',
920
+ writeKey,
921
+ {
922
+ apiKey: amplitudeWriteKey,
923
+ },
924
+ {}
925
+ )
926
+
927
+ const [analytics] = await HtEventsBrowser.load({
928
+ writeKey,
929
+ plugins: [amplitude],
930
+ })
931
+
932
+ await analytics.ready()
933
+
934
+ const scriptsLength = window.document.scripts.length
935
+ expect(scriptsLength).toBeGreaterThan(1)
936
+
937
+ await analytics.deregister('amplitude')
938
+ expect(window.document.scripts.length).toBe(scriptsLength - 1)
939
+ })
940
+ })
941
+
942
+ describe('retries', () => {
943
+ const testPlugin: Plugin = {
944
+ name: 'test',
945
+ type: 'before',
946
+ version: '0.1.0',
947
+ load: () => Promise.resolve(),
948
+ isLoaded: () => true,
949
+ }
950
+
951
+ const fruitBasketEvent = new Context({
952
+ type: 'track',
953
+ event: 'Fruit Basket',
954
+ })
955
+
956
+ beforeEach(async () => {
957
+ // @ts-ignore ignore reassining function
958
+ loadLegacySettings = jest.fn().mockReturnValue(
959
+ Promise.resolve({
960
+ integrations: { 'Hightouch.io': { retryQueue: false } },
961
+ })
962
+ )
963
+ })
964
+
965
+ it('does not retry errored events if retryQueue setting is set to false', async () => {
966
+ const [ajs] = await HtEventsBrowser.load(
967
+ { writeKey: writeKey },
968
+ { retryQueue: false }
969
+ )
970
+
971
+ expect(ajs.queue.queue instanceof PersistedPriorityQueue).toBeTruthy()
972
+ expect(ajs.queue.queue.maxAttempts).toBe(1)
973
+
974
+ await ajs.queue.register(
975
+ Context.system(),
976
+ {
977
+ ...testPlugin,
978
+ track: (_ctx) => {
979
+ throw new Error('aaay')
980
+ },
981
+ },
982
+ ajs
983
+ )
984
+
985
+ // Dispatching an event will push it into the priority queue.
986
+ await ajs.queue.dispatch(fruitBasketEvent).catch(() => {})
987
+
988
+ // we make sure the queue is flushed and there are no events queued up.
989
+ expect(ajs.queue.queue.length).toBe(0)
990
+ const flushed = await ajs.queue.flush()
991
+ expect(flushed).toStrictEqual([])
992
+
993
+ // as maxAttempts === 1, only one attempt was made.
994
+ // getAttempts(fruitBasketEvent) === 2 means the event's attemp was incremented,
995
+ // but the condition "(getAttempts(event) > maxAttempts) { return false }"
996
+ // aborted the retry
997
+ expect(ajs.queue.queue.getAttempts(fruitBasketEvent)).toEqual(2)
998
+ })
999
+
1000
+ it('does not queue events / dispatch when offline if retryQueue setting is set to false', async () => {
1001
+ const [ajs] = await HtEventsBrowser.load(
1002
+ { writeKey },
1003
+ { retryQueue: false }
1004
+ )
1005
+
1006
+ const trackSpy = jest.fn().mockImplementation((ctx) => ctx)
1007
+ await ajs.queue.register(
1008
+ Context.system(),
1009
+ {
1010
+ ...testPlugin,
1011
+ ready: () => Promise.resolve(true),
1012
+ track: trackSpy,
1013
+ },
1014
+ ajs
1015
+ )
1016
+
1017
+ // @ts-ignore ignore reassining function
1018
+ isOffline = jest.fn().mockReturnValue(true)
1019
+
1020
+ await ajs.track('event')
1021
+
1022
+ expect(trackSpy).toBeCalledTimes(0)
1023
+ })
1024
+
1025
+ it('enqueues events / dispatches if the client is currently offline and retries are *enabled* for the main event queue', async () => {
1026
+ const [ajs] = await HtEventsBrowser.load({ writeKey }, { retryQueue: true })
1027
+
1028
+ const trackSpy = jest.fn().mockImplementation((ctx) => ctx)
1029
+ await ajs.queue.register(
1030
+ Context.system(),
1031
+ {
1032
+ ...testPlugin,
1033
+ ready: () => Promise.resolve(true),
1034
+ track: trackSpy,
1035
+ },
1036
+ ajs
1037
+ )
1038
+
1039
+ // @ts-ignore ignore reassining function
1040
+ isOffline = jest.fn().mockReturnValue(true)
1041
+
1042
+ expect(trackSpy).toBeCalledTimes(0)
1043
+
1044
+ await ajs.track('event')
1045
+
1046
+ expect(trackSpy).toBeCalledTimes(1)
1047
+ })
1048
+ })
1049
+
1050
+ describe('Hightouch.io overrides', () => {
1051
+ it('allows for overriding Hightouch.io settings', async () => {
1052
+ jest.spyOn(HightouchPlugin, 'hightouchio')
1053
+
1054
+ await HtEventsBrowser.load(
1055
+ { writeKey },
1056
+ {
1057
+ integrations: {
1058
+ 'Hightouch.io': {
1059
+ apiHost: 'https://my.endpoint.com',
1060
+ anotherSettings: '👻',
1061
+ },
1062
+ },
1063
+ }
1064
+ )
1065
+
1066
+ expect(HightouchPlugin.hightouchio).toHaveBeenCalledWith(
1067
+ expect.anything(),
1068
+ expect.objectContaining({
1069
+ apiHost: 'https://my.endpoint.com',
1070
+ anotherSettings: '👻',
1071
+ }),
1072
+ expect.anything()
1073
+ )
1074
+ })
1075
+ })
1076
+
1077
+ // We do not support remote integrations
1078
+ describe.skip('Options', () => {
1079
+ beforeEach(async () => {
1080
+ jest.restoreAllMocks()
1081
+ jest.resetAllMocks()
1082
+
1083
+ const html = `
1084
+ <!DOCTYPE html>
1085
+ <head>
1086
+ <script>'hi'</script>
1087
+ </head>
1088
+ <body>
1089
+ </body>
1090
+ </html>
1091
+ `.trim()
1092
+
1093
+ const jsd = new JSDOM(html, {
1094
+ runScripts: 'dangerously',
1095
+ resources: 'usable',
1096
+ url: 'https://localhost',
1097
+ })
1098
+
1099
+ const windowSpy = jest.spyOn(global, 'window', 'get')
1100
+ windowSpy.mockImplementation(
1101
+ () => jsd.window as unknown as Window & typeof globalThis
1102
+ )
1103
+ })
1104
+
1105
+ describe('disableAutoISOConversion', () => {
1106
+ it('converts iso strings to dates be default', async () => {
1107
+ const [analytics] = await HtEventsBrowser.load({
1108
+ writeKey,
1109
+ })
1110
+
1111
+ const amplitude = new LegacyDestination(
1112
+ 'amplitude',
1113
+ 'latest',
1114
+ writeKey,
1115
+ {
1116
+ apiKey: amplitudeWriteKey,
1117
+ },
1118
+ {}
1119
+ )
1120
+
1121
+ await analytics.register(amplitude)
1122
+ await amplitude.ready()
1123
+
1124
+ const integrationMock = jest.spyOn(amplitude.integration!, 'track')
1125
+ await analytics.track('Hello!', {
1126
+ date: new Date(),
1127
+ iso: '2020-10-10',
1128
+ })
1129
+
1130
+ const [integrationEvent] = integrationMock.mock.lastCall
1131
+
1132
+ expect(integrationEvent.properties()).toEqual({
1133
+ date: expect.any(Date),
1134
+ iso: expect.any(Date),
1135
+ })
1136
+ expect(integrationEvent.timestamp()).toBeInstanceOf(Date)
1137
+ })
1138
+
1139
+ it('does not convert iso strings to dates be default if disableAutoISOConversion is false', async () => {
1140
+ const initOptions: InitOptions = { disableAutoISOConversion: false }
1141
+ const [analytics] = await HtEventsBrowser.load(
1142
+ {
1143
+ writeKey,
1144
+ },
1145
+ initOptions
1146
+ )
1147
+
1148
+ const amplitude = new LegacyDestination(
1149
+ 'amplitude',
1150
+ 'latest',
1151
+ writeKey,
1152
+ {
1153
+ apiKey: amplitudeWriteKey,
1154
+ },
1155
+ initOptions
1156
+ )
1157
+
1158
+ await analytics.register(amplitude)
1159
+ await amplitude.ready()
1160
+
1161
+ const integrationMock = jest.spyOn(amplitude.integration!, 'track')
1162
+ await analytics.track('Hello!', {
1163
+ date: new Date(),
1164
+ iso: '2020-10-10',
1165
+ })
1166
+
1167
+ const [integrationEvent] = integrationMock.mock.lastCall
1168
+
1169
+ expect(integrationEvent.properties()).toEqual({
1170
+ date: expect.any(Date),
1171
+ iso: expect.any(Date),
1172
+ })
1173
+ expect(integrationEvent.timestamp()).toBeInstanceOf(Date)
1174
+ })
1175
+
1176
+ it('does not convert iso strings to dates when `true`', async () => {
1177
+ const initOptions: InitOptions = { disableAutoISOConversion: true }
1178
+ const [analytics] = await HtEventsBrowser.load(
1179
+ {
1180
+ writeKey,
1181
+ },
1182
+ initOptions
1183
+ )
1184
+
1185
+ const amplitude = new LegacyDestination(
1186
+ 'amplitude',
1187
+ 'latest',
1188
+ writeKey,
1189
+ {
1190
+ apiKey: amplitudeWriteKey,
1191
+ },
1192
+ initOptions
1193
+ )
1194
+
1195
+ await analytics.register(amplitude)
1196
+ await amplitude.ready()
1197
+
1198
+ const integrationMock = jest.spyOn(amplitude.integration!, 'track')
1199
+ await analytics.track('Hello!', {
1200
+ date: new Date(),
1201
+ iso: '2020-10-10',
1202
+ })
1203
+
1204
+ const [integrationEvent] = integrationMock.mock.lastCall
1205
+
1206
+ expect(integrationEvent.properties()).toEqual({
1207
+ date: expect.any(Date),
1208
+ iso: '2020-10-10',
1209
+ })
1210
+ expect(integrationEvent.timestamp()).toBeInstanceOf(Date)
1211
+ })
1212
+ })
1213
+ })