@newrelic/browser-agent 1.232.0 → 1.233.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/dist/cjs/cdn/polyfills.js +5 -2
  2. package/dist/cjs/common/config/state/configurable.js +15 -26
  3. package/dist/cjs/common/config/state/info.js +1 -1
  4. package/dist/cjs/common/config/state/init.js +101 -56
  5. package/dist/cjs/common/config/state/loader-config.js +1 -1
  6. package/dist/cjs/common/config/state/runtime.js +1 -5
  7. package/dist/cjs/common/constants/env.cdn.js +1 -1
  8. package/dist/cjs/common/constants/env.npm.js +1 -1
  9. package/dist/cjs/common/drain/drain.js +1 -1
  10. package/dist/cjs/common/harvest/harvest-scheduler.js +30 -10
  11. package/dist/cjs/common/harvest/harvest.js +119 -55
  12. package/dist/cjs/common/session/session-entity.js +35 -22
  13. package/dist/cjs/common/session/session-entity.test.js +73 -49
  14. package/dist/cjs/common/timer/interaction-timer.js +9 -12
  15. package/dist/cjs/common/url/canonicalize-url.js +32 -0
  16. package/dist/cjs/common/url/canonicalize-url.test.js +42 -0
  17. package/dist/cjs/common/url/clean-url.js +10 -3
  18. package/dist/cjs/common/url/protocol.test.js +0 -1
  19. package/dist/cjs/common/util/feature-flags.js +2 -1
  20. package/dist/cjs/common/util/global-scope.js +4 -2
  21. package/dist/cjs/common/util/submit-data.js +57 -18
  22. package/dist/cjs/common/wrap/wrap-fetch.js +1 -3
  23. package/dist/cjs/common/wrap/wrap-function.js +1 -3
  24. package/dist/cjs/common/wrap/wrap-promise.js +1 -1
  25. package/dist/cjs/features/ajax/aggregate/index.js +2 -2
  26. package/dist/cjs/features/ajax/instrument/index.js +1 -1
  27. package/dist/cjs/features/jserrors/aggregate/canonical-function-name.js +12 -4
  28. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +93 -10
  29. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.test.js +164 -38
  30. package/dist/cjs/features/jserrors/aggregate/index.js +29 -48
  31. package/dist/cjs/features/jserrors/instrument/index.js +0 -2
  32. package/dist/cjs/features/metrics/aggregate/framework-detection.js +67 -0
  33. package/dist/cjs/features/metrics/aggregate/framework-detection.test.js +137 -0
  34. package/dist/cjs/features/metrics/aggregate/index.js +7 -3
  35. package/dist/cjs/features/metrics/aggregate/polyfill-detection.es5.js +14 -0
  36. package/dist/cjs/features/metrics/aggregate/polyfill-detection.es5.test.js +17 -0
  37. package/dist/cjs/features/metrics/aggregate/polyfill-detection.js +53 -0
  38. package/dist/cjs/features/metrics/aggregate/polyfill-detection.test.js +165 -0
  39. package/dist/cjs/features/page_action/aggregate/index.js +2 -2
  40. package/dist/cjs/features/page_view_event/aggregate/index.js +6 -3
  41. package/dist/cjs/features/page_view_timing/aggregate/index.js +2 -2
  42. package/dist/cjs/features/session_replay/aggregate/index.js +333 -0
  43. package/dist/cjs/features/session_replay/constants.js +9 -0
  44. package/dist/cjs/features/session_replay/index.js +12 -0
  45. package/dist/cjs/features/session_replay/instrument/index.js +29 -0
  46. package/dist/cjs/features/session_trace/aggregate/index.js +163 -164
  47. package/dist/cjs/features/session_trace/constants.js +2 -9
  48. package/dist/cjs/features/session_trace/instrument/index.js +24 -66
  49. package/dist/cjs/features/spa/aggregate/index.js +2 -2
  50. package/dist/cjs/features/utils/agent-session.js +1 -2
  51. package/dist/cjs/features/utils/aggregate-base.js +64 -0
  52. package/dist/cjs/features/utils/feature-base.js +0 -31
  53. package/dist/cjs/features/utils/handler-cache.js +3 -4
  54. package/dist/cjs/features/utils/instrument-base.js +42 -10
  55. package/dist/cjs/features/utils/{lazy-loader.js → lazy-feature-loader.js} +4 -2
  56. package/dist/cjs/loaders/agent.js +1 -1
  57. package/dist/cjs/loaders/api/apiAsync.js +3 -1
  58. package/dist/cjs/loaders/configure/configure.js +3 -3
  59. package/dist/cjs/loaders/features/featureDependencies.js +0 -12
  60. package/dist/cjs/loaders/features/features.js +3 -1
  61. package/dist/cjs/loaders/micro-agent.js +6 -6
  62. package/dist/esm/cdn/polyfills.js +5 -2
  63. package/dist/esm/common/config/state/configurable.js +14 -24
  64. package/dist/esm/common/config/state/info.js +2 -2
  65. package/dist/esm/common/config/state/init.js +102 -57
  66. package/dist/esm/common/config/state/loader-config.js +2 -2
  67. package/dist/esm/common/config/state/runtime.js +2 -4
  68. package/dist/esm/common/constants/env.cdn.js +1 -1
  69. package/dist/esm/common/constants/env.npm.js +1 -1
  70. package/dist/esm/common/drain/drain.js +1 -1
  71. package/dist/esm/common/harvest/harvest-scheduler.js +30 -10
  72. package/dist/esm/common/harvest/harvest.js +121 -56
  73. package/dist/esm/common/session/session-entity.js +35 -22
  74. package/dist/esm/common/session/session-entity.test.js +73 -49
  75. package/dist/esm/common/timer/interaction-timer.js +9 -12
  76. package/dist/esm/common/url/canonicalize-url.js +27 -0
  77. package/dist/esm/common/url/canonicalize-url.test.js +38 -0
  78. package/dist/esm/common/url/clean-url.js +10 -3
  79. package/dist/esm/common/url/protocol.test.js +0 -1
  80. package/dist/esm/common/util/feature-flags.js +2 -1
  81. package/dist/esm/common/util/global-scope.js +1 -0
  82. package/dist/esm/common/util/submit-data.js +57 -18
  83. package/dist/esm/common/wrap/wrap-fetch.js +1 -2
  84. package/dist/esm/common/wrap/wrap-function.js +1 -2
  85. package/dist/esm/common/wrap/wrap-promise.js +1 -1
  86. package/dist/esm/features/ajax/aggregate/index.js +2 -2
  87. package/dist/esm/features/ajax/instrument/index.js +1 -1
  88. package/dist/esm/features/jserrors/aggregate/canonical-function-name.js +12 -4
  89. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +93 -10
  90. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.test.js +149 -25
  91. package/dist/esm/features/jserrors/aggregate/index.js +30 -48
  92. package/dist/esm/features/jserrors/instrument/index.js +0 -1
  93. package/dist/esm/features/metrics/aggregate/framework-detection.js +61 -0
  94. package/dist/esm/features/metrics/aggregate/framework-detection.test.js +133 -0
  95. package/dist/esm/features/metrics/aggregate/index.js +7 -3
  96. package/dist/esm/features/metrics/aggregate/polyfill-detection.es5.js +8 -0
  97. package/dist/esm/features/metrics/aggregate/polyfill-detection.es5.test.js +15 -0
  98. package/dist/esm/features/metrics/aggregate/polyfill-detection.js +47 -0
  99. package/dist/esm/features/metrics/aggregate/polyfill-detection.test.js +163 -0
  100. package/dist/esm/features/page_action/aggregate/index.js +2 -2
  101. package/dist/esm/features/page_view_event/aggregate/index.js +6 -3
  102. package/dist/esm/features/page_view_timing/aggregate/index.js +2 -2
  103. package/dist/esm/features/session_replay/aggregate/index.js +327 -0
  104. package/dist/esm/features/session_replay/constants.js +2 -0
  105. package/dist/esm/features/session_replay/index.js +12 -0
  106. package/dist/esm/features/session_replay/instrument/index.js +21 -0
  107. package/dist/esm/features/session_trace/aggregate/index.js +163 -163
  108. package/dist/esm/features/session_trace/constants.js +1 -5
  109. package/dist/esm/features/session_trace/instrument/index.js +24 -66
  110. package/dist/esm/features/spa/aggregate/index.js +2 -2
  111. package/dist/esm/features/utils/agent-session.js +1 -2
  112. package/dist/esm/features/utils/aggregate-base.js +57 -0
  113. package/dist/esm/features/utils/feature-base.js +1 -32
  114. package/dist/esm/features/utils/handler-cache.js +3 -4
  115. package/dist/esm/features/utils/instrument-base.js +42 -10
  116. package/dist/esm/features/utils/{lazy-loader.js → lazy-feature-loader.js} +3 -1
  117. package/dist/esm/loaders/agent.js +1 -1
  118. package/dist/esm/loaders/api/apiAsync.js +3 -1
  119. package/dist/esm/loaders/configure/configure.js +3 -3
  120. package/dist/esm/loaders/features/featureDependencies.js +0 -11
  121. package/dist/esm/loaders/features/features.js +3 -1
  122. package/dist/esm/loaders/micro-agent.js +6 -6
  123. package/dist/types/common/config/state/configurable.d.ts +1 -3
  124. package/dist/types/common/config/state/configurable.d.ts.map +1 -1
  125. package/dist/types/common/config/state/init.d.ts.map +1 -1
  126. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  127. package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
  128. package/dist/types/common/harvest/harvest.d.ts +37 -34
  129. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  130. package/dist/types/common/session/session-entity.d.ts +6 -3
  131. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  132. package/dist/types/common/timer/interaction-timer.d.ts +2 -1
  133. package/dist/types/common/timer/interaction-timer.d.ts.map +1 -1
  134. package/dist/types/common/url/canonicalize-url.d.ts +9 -0
  135. package/dist/types/common/url/canonicalize-url.d.ts.map +1 -0
  136. package/dist/types/common/url/clean-url.d.ts +7 -1
  137. package/dist/types/common/url/clean-url.d.ts.map +1 -1
  138. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  139. package/dist/types/common/util/global-scope.d.ts +1 -0
  140. package/dist/types/common/util/global-scope.d.ts.map +1 -1
  141. package/dist/types/common/util/submit-data.d.ts +40 -14
  142. package/dist/types/common/util/submit-data.d.ts.map +1 -1
  143. package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
  144. package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
  145. package/dist/types/features/ajax/aggregate/index.d.ts +2 -2
  146. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  147. package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts +8 -1
  148. package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts.map +1 -1
  149. package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts +48 -19
  150. package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts.map +1 -1
  151. package/dist/types/features/jserrors/aggregate/index.d.ts +14 -5
  152. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  153. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  154. package/dist/types/features/metrics/aggregate/framework-detection.d.ts.map +1 -0
  155. package/dist/types/features/metrics/aggregate/index.d.ts +2 -2
  156. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  157. package/dist/types/features/metrics/aggregate/polyfill-detection.d.ts +6 -0
  158. package/dist/types/features/metrics/aggregate/polyfill-detection.d.ts.map +1 -0
  159. package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts +7 -0
  160. package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts.map +1 -0
  161. package/dist/types/features/page_action/aggregate/index.d.ts +3 -3
  162. package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
  163. package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -2
  164. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  165. package/dist/types/features/page_view_timing/aggregate/index.d.ts +2 -2
  166. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  167. package/dist/types/features/session_replay/aggregate/index.d.ts +96 -0
  168. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -0
  169. package/dist/types/features/session_replay/constants.d.ts +2 -0
  170. package/dist/types/features/session_replay/constants.d.ts.map +1 -0
  171. package/dist/types/features/session_replay/index.d.ts +2 -0
  172. package/dist/types/features/session_replay/index.d.ts.map +1 -0
  173. package/dist/types/features/session_replay/instrument/index.d.ts +6 -0
  174. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -0
  175. package/dist/types/features/session_trace/aggregate/index.d.ts +8 -57
  176. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  177. package/dist/types/features/session_trace/constants.d.ts +0 -3
  178. package/dist/types/features/session_trace/constants.d.ts.map +1 -1
  179. package/dist/types/features/session_trace/instrument/index.d.ts +1 -3
  180. package/dist/types/features/session_trace/instrument/index.d.ts.map +1 -1
  181. package/dist/types/features/spa/aggregate/index.d.ts +2 -2
  182. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  183. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  184. package/dist/types/features/utils/aggregate-base.d.ts +11 -0
  185. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -0
  186. package/dist/types/features/utils/feature-base.d.ts +0 -5
  187. package/dist/types/features/utils/feature-base.d.ts.map +1 -1
  188. package/dist/types/features/utils/handler-cache.d.ts.map +1 -1
  189. package/dist/types/features/utils/instrument-base.d.ts +3 -1
  190. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  191. package/dist/types/features/utils/{lazy-loader.d.ts → lazy-feature-loader.d.ts} +2 -2
  192. package/dist/types/features/utils/lazy-feature-loader.d.ts.map +1 -0
  193. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  194. package/dist/types/loaders/features/featureDependencies.d.ts +0 -1
  195. package/dist/types/loaders/features/featureDependencies.d.ts.map +1 -1
  196. package/dist/types/loaders/features/features.d.ts +1 -0
  197. package/dist/types/loaders/features/features.d.ts.map +1 -1
  198. package/package.json +31 -22
  199. package/src/cdn/polyfills.js +4 -1
  200. package/src/common/config/state/configurable.js +18 -24
  201. package/src/common/config/state/info.js +2 -2
  202. package/src/common/config/state/init.js +62 -28
  203. package/src/common/config/state/loader-config.js +2 -2
  204. package/src/common/config/state/runtime.js +2 -4
  205. package/src/common/drain/drain.js +1 -1
  206. package/src/common/harvest/harvest-scheduler.js +35 -10
  207. package/src/common/harvest/harvest.js +73 -50
  208. package/src/common/session/session-entity.js +34 -23
  209. package/src/common/session/session-entity.test.js +57 -51
  210. package/src/common/timer/interaction-timer.js +9 -12
  211. package/src/common/url/canonicalize-url.js +28 -0
  212. package/src/common/url/canonicalize-url.test.js +34 -0
  213. package/src/common/url/clean-url.js +10 -3
  214. package/src/common/url/protocol.test.js +0 -1
  215. package/src/common/util/feature-flags.js +2 -2
  216. package/src/common/util/global-scope.js +2 -0
  217. package/src/common/util/submit-data.js +28 -17
  218. package/src/common/wrap/wrap-fetch.js +1 -2
  219. package/src/common/wrap/wrap-function.js +1 -2
  220. package/src/common/wrap/wrap-promise.js +1 -1
  221. package/src/features/ajax/aggregate/index.js +2 -2
  222. package/src/features/ajax/instrument/index.js +1 -1
  223. package/src/features/jserrors/aggregate/canonical-function-name.js +12 -4
  224. package/src/features/jserrors/aggregate/compute-stack-trace.js +85 -11
  225. package/src/features/jserrors/aggregate/compute-stack-trace.test.js +141 -24
  226. package/src/features/jserrors/aggregate/index.js +28 -52
  227. package/src/features/jserrors/instrument/index.js +0 -1
  228. package/src/features/metrics/aggregate/framework-detection.js +73 -0
  229. package/src/features/metrics/aggregate/framework-detection.test.js +201 -0
  230. package/src/features/metrics/aggregate/index.js +8 -3
  231. package/src/features/metrics/aggregate/polyfill-detection.es5.js +9 -0
  232. package/src/features/metrics/aggregate/polyfill-detection.es5.test.js +16 -0
  233. package/src/features/metrics/aggregate/polyfill-detection.js +48 -0
  234. package/src/features/metrics/aggregate/polyfill-detection.test.js +163 -0
  235. package/src/features/page_action/aggregate/index.js +2 -2
  236. package/src/features/page_view_event/aggregate/index.js +5 -5
  237. package/src/features/page_view_timing/aggregate/index.js +2 -2
  238. package/src/features/session_replay/aggregate/index.js +314 -0
  239. package/src/features/session_replay/constants.js +3 -0
  240. package/src/features/session_replay/index.js +12 -0
  241. package/src/features/session_replay/instrument/index.js +22 -0
  242. package/src/features/session_trace/aggregate/index.js +148 -188
  243. package/src/features/session_trace/constants.js +0 -4
  244. package/src/features/session_trace/instrument/index.js +17 -69
  245. package/src/features/spa/aggregate/index.js +2 -2
  246. package/src/features/utils/agent-session.js +1 -2
  247. package/src/features/utils/aggregate-base.js +51 -0
  248. package/src/features/utils/feature-base.js +1 -31
  249. package/src/features/utils/handler-cache.js +3 -4
  250. package/src/features/utils/instrument-base.js +40 -8
  251. package/src/features/utils/{lazy-loader.js → lazy-feature-loader.js} +3 -1
  252. package/src/loaders/agent.js +1 -1
  253. package/src/loaders/api/apiAsync.js +1 -1
  254. package/src/loaders/configure/configure.js +4 -3
  255. package/src/loaders/features/featureDependencies.js +0 -12
  256. package/src/loaders/features/features.js +3 -1
  257. package/src/loaders/micro-agent.js +4 -4
  258. package/dist/cjs/common/metrics/framework-detection.js +0 -72
  259. package/dist/cjs/common/util/user-agent.js +0 -57
  260. package/dist/cjs/common/window/supports-performance-observer.js +0 -15
  261. package/dist/esm/common/metrics/framework-detection.js +0 -66
  262. package/dist/esm/common/util/user-agent.js +0 -48
  263. package/dist/esm/common/window/supports-performance-observer.js +0 -9
  264. package/dist/types/common/metrics/framework-detection.d.ts.map +0 -1
  265. package/dist/types/common/util/user-agent.d.ts +0 -5
  266. package/dist/types/common/util/user-agent.d.ts.map +0 -1
  267. package/dist/types/common/window/supports-performance-observer.d.ts +0 -2
  268. package/dist/types/common/window/supports-performance-observer.d.ts.map +0 -1
  269. package/dist/types/features/utils/lazy-loader.d.ts.map +0 -1
  270. package/src/common/metrics/framework-detection.js +0 -71
  271. package/src/common/util/user-agent.js +0 -56
  272. package/src/common/window/supports-performance-observer.js +0 -10
  273. /package/dist/types/{common/metrics → features/metrics/aggregate}/framework-detection.d.ts +0 -0
@@ -1,14 +1,27 @@
1
1
  import { faker } from '@faker-js/faker';
2
2
  import { browserErrorUtils } from '../../../../tools/testing-utils';
3
- import { computeStackTrace } from './compute-stack-trace';
3
+ const globalScopeLocation = 'https://example.com/';
4
+ const mockGlobalScopeLocation = url => {
5
+ jest.doMock('../../../common/util/global-scope', () => ({
6
+ initialLocation: url || globalScopeLocation
7
+ }));
8
+ };
9
+ afterEach(() => {
10
+ jest.resetModules();
11
+ jest.clearAllMocks();
12
+ });
4
13
  const baseMockError = {
5
14
  toString: 'RangeError: Invalid array length',
6
15
  name: 'RangeError',
7
16
  constructor: 'function RangeError() { [native code] }',
8
17
  message: 'Invalid array length',
9
- stack: 'RangeError: Invalid array length\n at errorTest (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:74:16)\n at captureError (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:17:9)\n at onload (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:70:5)'
18
+ stack: 'RangeError: Invalid array length\n' + ' at errorTest (' + globalScopeLocation + '?loader=spa#hello:74:16)\n' + ' at captureError (' + globalScopeLocation + 'js/script.js?loader=spa:17:9)\n' + ' at onload (' + globalScopeLocation + 'js/script.js?loader=spa:70:5)'
10
19
  };
11
- test('parsing should return a failure for a null error object', () => {
20
+ test('parsing should return a failure for a null error object', async () => {
21
+ mockGlobalScopeLocation();
22
+ const {
23
+ computeStackTrace
24
+ } = await import('./compute-stack-trace');
12
25
  const result = computeStackTrace(null);
13
26
  expect(result).toEqual(expect.objectContaining({
14
27
  mode: 'failed',
@@ -17,24 +30,45 @@ test('parsing should return a failure for a null error object', () => {
17
30
  }));
18
31
  });
19
32
  describe('errors with stack property', () => {
20
- test('parsed name should be unknown when name and constructor are missing', () => {
33
+ test('should show <inline> for same-page stack string URLs but not sub-paths', async () => {
34
+ const mockError = browserErrorUtils.constructError({
35
+ ...baseMockError
36
+ });
37
+ mockGlobalScopeLocation();
38
+ const {
39
+ computeStackTrace
40
+ } = await import('./compute-stack-trace');
41
+ const result = computeStackTrace(mockError);
42
+ expect(result).toEqual(expect.objectContaining({
43
+ stackString:
44
+ // canonicalized
45
+ 'RangeError: Invalid array length\n' + ' at errorTest (<inline>:74:16)\n' + ' at captureError (' + globalScopeLocation + 'js/script.js:17:9)\n' + ' at onload (' + globalScopeLocation + 'js/script.js:70:5)'
46
+ }));
47
+ });
48
+ test('parsed name should be unknown when name and constructor are missing', async () => {
21
49
  const mockError = browserErrorUtils.constructError({
22
50
  ...baseMockError,
23
51
  name: null,
24
52
  constructor: null
25
53
  });
54
+ mockGlobalScopeLocation();
55
+ const {
56
+ computeStackTrace
57
+ } = await import('./compute-stack-trace');
26
58
  const result = computeStackTrace(mockError);
27
59
  expect(result).toEqual(expect.objectContaining({
28
60
  mode: 'stack',
29
- name: 'unknown',
30
- message: mockError.message,
31
- stackString: mockError.stack
61
+ name: 'unknown'
32
62
  }));
33
63
  });
34
- test('parsed stack should not contain nrWrapper', () => {
64
+ test('parsed stack should not contain nrWrapper', async () => {
35
65
  const alteredError = baseMockError;
36
- alteredError.stack += '\n at nrWrapper (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:60:17)';
66
+ alteredError.stack += '\n at nrWrapper (' + globalScopeLocation + '?loader=spa:60:17)';
37
67
  const mockError = browserErrorUtils.constructError(alteredError);
68
+ mockGlobalScopeLocation();
69
+ const {
70
+ computeStackTrace
71
+ } = await import('./compute-stack-trace');
38
72
  const result = computeStackTrace(mockError);
39
73
  expect(result).toEqual(expect.objectContaining({
40
74
  mode: 'stack',
@@ -46,17 +80,21 @@ describe('errors with stack property', () => {
46
80
  func: 'nrWrapper'
47
81
  }));
48
82
  });
49
- test('stack should still parse when column numbers are missing', () => {
83
+ test('stack should still parse when column numbers are missing', async () => {
50
84
  const mockError = browserErrorUtils.constructError({
51
85
  ...baseMockError,
52
- stack: 'Error: Blocked a frame with origin "http://bam-test-1.nr-local.net:3334" from accessing a cross-origin frame.\n at errorTest (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:60)\n at captureError (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:17)\n at onload (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:57)'
86
+ stack: 'RangeError: Invalid array length\n' + 'Error: Blocked a frame with origin "http://bam-test-1.nr-local.net:3334" from accessing a cross-origin frame.\n' + ' at errorTest (' + globalScopeLocation + '?loader=spa:60)\n' + ' at captureError (' + globalScopeLocation + '?loader=spa:17)\n' + ' at onload (' + globalScopeLocation + '?loader=spa:57)'
53
87
  });
88
+ mockGlobalScopeLocation();
89
+ const {
90
+ computeStackTrace
91
+ } = await import('./compute-stack-trace');
54
92
  const result = computeStackTrace(mockError);
55
93
  expect(result).toEqual(expect.objectContaining({
56
94
  mode: 'stack',
57
95
  name: mockError.name,
58
96
  message: mockError.message,
59
- stackString: mockError.stack
97
+ stackString: 'RangeError: Invalid array length\n' + 'Error: Blocked a frame with origin "http://bam-test-1.nr-local.net:3334" from accessing a cross-origin frame.\n' + ' at errorTest (<inline>:60)\n' + ' at captureError (<inline>:17)\n' + ' at onload (<inline>:57)'
60
98
  }));
61
99
  expect(result.frames.length).toEqual(3);
62
100
  expect(result.frames).toContainEqual(expect.objectContaining({
@@ -72,11 +110,15 @@ describe('errors with stack property', () => {
72
110
  column: null
73
111
  }));
74
112
  });
75
- test('parser can handle chrome eval stack', () => {
113
+ test('parser can handle chrome eval stack', async () => {
76
114
  const mockError = browserErrorUtils.constructError({
77
115
  ...baseMockError,
78
- stack: ' at foobar (eval at foobar (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html))'
116
+ stack: ' at foobar (eval at foobar (' + globalScopeLocation + '))'
79
117
  });
118
+ mockGlobalScopeLocation();
119
+ const {
120
+ computeStackTrace
121
+ } = await import('./compute-stack-trace');
80
122
  const result = computeStackTrace(mockError);
81
123
  expect(result).toEqual(expect.objectContaining({
82
124
  mode: 'stack',
@@ -89,7 +131,7 @@ describe('errors with stack property', () => {
89
131
  func: 'evaluated code'
90
132
  }));
91
133
  });
92
- test('parser can handle ie eval stack', () => {
134
+ test('parser can handle ie eval stack', async () => {
93
135
  const mockError = browserErrorUtils.constructError({
94
136
  toString: 'TypeError: Permission denied',
95
137
  name: 'TypeError',
@@ -97,6 +139,10 @@ describe('errors with stack property', () => {
97
139
  message: 'Permission denied',
98
140
  stack: ' at Function code (Function code:23:23)'
99
141
  });
142
+ mockGlobalScopeLocation();
143
+ const {
144
+ computeStackTrace
145
+ } = await import('./compute-stack-trace');
100
146
  const result = computeStackTrace(mockError);
101
147
  expect(result).toEqual(expect.objectContaining({
102
148
  mode: 'stack',
@@ -109,11 +155,15 @@ describe('errors with stack property', () => {
109
155
  func: 'evaluated code'
110
156
  }));
111
157
  });
112
- test('parser can handle stack with anonymous function', () => {
158
+ test('parser can handle stack with anonymous function', async () => {
113
159
  const mockError = browserErrorUtils.constructError({
114
160
  ...baseMockError,
115
161
  stack: 'anonymous'
116
162
  });
163
+ mockGlobalScopeLocation();
164
+ const {
165
+ computeStackTrace
166
+ } = await import('./compute-stack-trace');
117
167
  const result = computeStackTrace(mockError);
118
168
  expect(result).toEqual(expect.objectContaining({
119
169
  mode: 'stack',
@@ -131,7 +181,7 @@ describe('errors without stack property and with line property', () => {
131
181
  /**
132
182
  * @deprecated sourceURL is no longer present in errors for any browsers we support
133
183
  */
134
- test('parsed stack should contain sourceURL and line number', () => {
184
+ test('parsed stack should contain sourceURL and line number', async () => {
135
185
  const sourceURL = faker.internet.url();
136
186
  const mockError = browserErrorUtils.constructError({
137
187
  ...baseMockError,
@@ -139,6 +189,10 @@ describe('errors without stack property and with line property', () => {
139
189
  line: 100,
140
190
  sourceURL
141
191
  });
192
+ mockGlobalScopeLocation();
193
+ const {
194
+ computeStackTrace
195
+ } = await import('./compute-stack-trace');
142
196
  const result = computeStackTrace(mockError);
143
197
  expect(result).toEqual(expect.objectContaining({
144
198
  mode: 'sourceline',
@@ -156,7 +210,7 @@ describe('errors without stack property and with line property', () => {
156
210
  /**
157
211
  * @deprecated sourceURL is no longer present in errors for any browsers we support
158
212
  */
159
- test('parsed stack should contain sourceURL, line number, and column number', () => {
213
+ test('parsed stack should contain sourceURL, line number, and column number', async () => {
160
214
  const sourceURL = faker.internet.url();
161
215
  const mockError = browserErrorUtils.constructError({
162
216
  ...baseMockError,
@@ -165,6 +219,10 @@ describe('errors without stack property and with line property', () => {
165
219
  stack: undefined,
166
220
  sourceURL
167
221
  });
222
+ mockGlobalScopeLocation();
223
+ const {
224
+ computeStackTrace
225
+ } = await import('./compute-stack-trace');
168
226
  const result = computeStackTrace(mockError);
169
227
  expect(result).toEqual(expect.objectContaining({
170
228
  mode: 'sourceline',
@@ -179,13 +237,17 @@ describe('errors without stack property and with line property', () => {
179
237
  column: mockError.column
180
238
  }));
181
239
  });
182
- test('parsed stack should contain "evaluated code" if sourceURL property is not present', () => {
240
+ test('parsed stack should contain "evaluated code" if sourceURL property is not present', async () => {
183
241
  const mockError = browserErrorUtils.constructError({
184
242
  ...baseMockError,
185
243
  line: 100,
186
244
  column: 200,
187
245
  stack: undefined
188
246
  });
247
+ mockGlobalScopeLocation();
248
+ const {
249
+ computeStackTrace
250
+ } = await import('./compute-stack-trace');
189
251
  const result = computeStackTrace(mockError);
190
252
  expect(result).toEqual(expect.objectContaining({
191
253
  mode: 'sourceline',
@@ -199,6 +261,58 @@ describe('errors without stack property and with line property', () => {
199
261
  }));
200
262
  });
201
263
 
264
+ /**
265
+ * @deprecated sourceURL is no longer present in errors for any browsers we support
266
+ */
267
+ test('should show <inline> for same-page URLs', async () => {
268
+ const pageLocation = faker.internet.url();
269
+ const sourceURL = pageLocation + '?abc=123';
270
+ const mockError = browserErrorUtils.constructError({
271
+ ...baseMockError,
272
+ line: 100,
273
+ column: 200,
274
+ stack: undefined,
275
+ sourceURL: sourceURL
276
+ });
277
+ mockGlobalScopeLocation(pageLocation);
278
+ const {
279
+ computeStackTrace
280
+ } = await import('./compute-stack-trace');
281
+ const result = computeStackTrace(mockError);
282
+ expect(result).toEqual(expect.objectContaining({
283
+ stackString: "".concat(mockError.name, ": ").concat(mockError.message, "\n at <inline>:").concat(mockError.line, ":").concat(mockError.column)
284
+ }));
285
+ expect(result.frames).toContainEqual(expect.objectContaining({
286
+ url: '<inline>'
287
+ }));
288
+ });
289
+
290
+ /**
291
+ * @deprecated sourceURL is no longer present in errors for any browsers we support
292
+ */
293
+ test('should NOT show <inline> for same-domain URLs with a sub-path', async () => {
294
+ const pageLocation = faker.internet.url();
295
+ const sourceURL = pageLocation + '/path/to/script.js';
296
+ const mockError = browserErrorUtils.constructError({
297
+ ...baseMockError,
298
+ line: 100,
299
+ column: 200,
300
+ stack: undefined,
301
+ sourceURL
302
+ });
303
+ mockGlobalScopeLocation(pageLocation);
304
+ const {
305
+ computeStackTrace
306
+ } = await import('./compute-stack-trace');
307
+ const result = computeStackTrace(mockError);
308
+ expect(result).toEqual(expect.objectContaining({
309
+ stackString: "".concat(mockError.name, ": ").concat(mockError.message, "\n at ").concat(sourceURL, ":").concat(mockError.line, ":").concat(mockError.column)
310
+ }));
311
+ expect(result.frames).toContainEqual(expect.objectContaining({
312
+ url: sourceURL
313
+ }));
314
+ });
315
+
202
316
  // TODO: computeStackTraceBySourceAndLine does not respect firefox lineNumber and columnNumber properties when stack is empty
203
317
  });
204
318
 
@@ -207,11 +321,15 @@ describe('errors without stack property and with line property', () => {
207
321
  * error, including primitives.
208
322
  */
209
323
  describe('errors that are messages only or primitives', () => {
210
- test('parser should get error name from constructor', () => {
324
+ test('parser should get error name from constructor', async () => {
211
325
  const mockError = browserErrorUtils.constructError({
212
326
  toString: '0',
213
327
  constructor: 'function Number() { [native code] }'
214
328
  });
329
+ mockGlobalScopeLocation();
330
+ const {
331
+ computeStackTrace
332
+ } = await import('./compute-stack-trace');
215
333
  const result = computeStackTrace(mockError);
216
334
  expect(result).toEqual(expect.objectContaining({
217
335
  mode: 'nameonly',
@@ -220,12 +338,16 @@ describe('errors that are messages only or primitives', () => {
220
338
  frames: []
221
339
  }));
222
340
  });
223
- test('parser should get error name from name property', () => {
341
+ test('parser should get error name from name property', async () => {
224
342
  const mockError = browserErrorUtils.constructError({
225
343
  toString: '0',
226
344
  name: faker.datatype.uuid(),
227
345
  constructor: 'function Number() { [native code] }'
228
346
  });
347
+ mockGlobalScopeLocation();
348
+ const {
349
+ computeStackTrace
350
+ } = await import('./compute-stack-trace');
229
351
  const result = computeStackTrace(mockError);
230
352
  expect(result).toEqual(expect.objectContaining({
231
353
  mode: 'nameonly',
@@ -234,13 +356,17 @@ describe('errors that are messages only or primitives', () => {
234
356
  frames: []
235
357
  }));
236
358
  });
237
- test('parser should include the message property', () => {
359
+ test('parser should include the message property', async () => {
238
360
  const mockError = browserErrorUtils.constructError({
239
361
  toString: '0',
240
362
  name: faker.datatype.uuid(),
241
363
  message: faker.datatype.uuid(),
242
364
  constructor: 'function Number() { [native code] }'
243
365
  });
366
+ mockGlobalScopeLocation();
367
+ const {
368
+ computeStackTrace
369
+ } = await import('./compute-stack-trace');
244
370
  const result = computeStackTrace(mockError);
245
371
  expect(result).toEqual(expect.objectContaining({
246
372
  mode: 'nameonly',
@@ -250,6 +376,4 @@ describe('errors that are messages only or primitives', () => {
250
376
  frames: []
251
377
  }));
252
378
  });
253
- });
254
-
255
- // describe('')
379
+ });
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  import { canonicalFunctionName } from './canonical-function-name';
7
- import { cleanURL } from '../../../common/url/clean-url';
8
7
  import { computeStackTrace } from './compute-stack-trace';
9
8
  import { stringHashCode } from './string-hash-code';
10
9
  import { truncateSize } from './format-stack-trace';
@@ -19,8 +18,13 @@ import { globalScope } from '../../../common/util/global-scope';
19
18
  import { FEATURE_NAME } from '../constants';
20
19
  import { drain } from '../../../common/drain/drain';
21
20
  import { FEATURE_NAMES } from '../../../loaders/features/features';
22
- import { FeatureBase } from '../../utils/feature-base';
23
- export class Aggregate extends FeatureBase {
21
+ import { AggregateBase } from '../../utils/aggregate-base';
22
+
23
+ /**
24
+ * @typedef {import('./compute-stack-trace.js').StackInfo} StackInfo
25
+ */
26
+
27
+ export class Aggregate extends AggregateBase {
24
28
  static featureName = FEATURE_NAME;
25
29
  constructor(agentIdentifier, aggregator) {
26
30
  var _this;
@@ -103,49 +107,25 @@ export class Aggregate extends FeatureBase {
103
107
  getBucketName(params, customParams) {
104
108
  return this.nameHash(params) + ':' + stringHashCode(stringify(customParams));
105
109
  }
106
- canonicalizeURL(url, cleanedOrigin) {
107
- if (typeof url !== 'string') return '';
108
- var cleanedURL = cleanURL(url);
109
- if (cleanedURL === cleanedOrigin) {
110
- return '<inline>';
111
- } else {
112
- return cleanedURL;
113
- }
114
- }
115
- buildCanonicalStackString(stackInfo, cleanedOrigin) {
116
- var canonicalStack = '';
117
- for (var i = 0; i < stackInfo.frames.length; i++) {
118
- var frame = stackInfo.frames[i];
119
- var func = canonicalFunctionName(frame.func);
120
- if (canonicalStack) canonicalStack += '\n';
121
- if (func) canonicalStack += func + '@';
122
- if (typeof frame.url === 'string') canonicalStack += frame.url;
123
- if (frame.line) canonicalStack += ':' + frame.line;
124
- }
125
- return canonicalStack;
126
- }
127
110
 
128
- // Strip query parameters and fragments from the stackString property of the
129
- // given stackInfo, along with the 'url' properties of each frame in
130
- // stackInfo.frames.
131
- //
132
- // Any URLs that are equivalent to the cleaned version of the origin will also
133
- // be replaced with the string '<inline>'.
134
- //
135
- canonicalizeStackURLs(stackInfo) {
136
- // Currently, loader.origin might contain a fragment, but we don't want to use it
137
- // for comparing with frame URLs.
138
- var cleanedOrigin = cleanURL(getRuntime(this.agentIdentifier).origin);
111
+ /**
112
+ * Builds a standardized stack trace string from the frames in the given `stackInfo` object, with each frame separated
113
+ * by a newline character. Lines take the form `<functionName>@<url>:<lineNumber>`.
114
+ *
115
+ * @param {StackInfo} stackInfo - An object specifying a stack string and individual frames.
116
+ * @returns {string} A canonical stack string built from the URLs and function names in the given `stackInfo` object.
117
+ */
118
+ buildCanonicalStackString(stackInfo) {
119
+ var canonicalStackString = '';
139
120
  for (var i = 0; i < stackInfo.frames.length; i++) {
140
121
  var frame = stackInfo.frames[i];
141
- var originalURL = frame.url;
142
- var cleanedURL = this.canonicalizeURL(originalURL, cleanedOrigin);
143
- if (cleanedURL && cleanedURL !== frame.url) {
144
- frame.url = cleanedURL;
145
- stackInfo.stackString = stackInfo.stackString.split(originalURL).join(cleanedURL);
146
- }
122
+ var func = canonicalFunctionName(frame.func);
123
+ if (canonicalStackString) canonicalStackString += '\n';
124
+ if (func) canonicalStackString += func + '@';
125
+ if (typeof frame.url === 'string') canonicalStackString += frame.url;
126
+ if (frame.line) canonicalStackString += ':' + frame.line;
147
127
  }
148
- return stackInfo;
128
+ return canonicalStackString;
149
129
  }
150
130
  storeError(err, time, internal, customAttributes) {
151
131
  // are we in an interaction
@@ -162,10 +142,10 @@ export class Aggregate extends FeatureBase {
162
142
  // Again as with previous usage, all falsey values would include the error.
163
143
  }
164
144
 
165
- var stackInfo = this.canonicalizeStackURLs(computeStackTrace(err));
166
- var canonicalStack = this.buildCanonicalStackString(stackInfo);
145
+ var stackInfo = computeStackTrace(err);
146
+ var canonicalStackString = this.buildCanonicalStackString(stackInfo);
167
147
  const params = {
168
- stackHash: stringHashCode(canonicalStack),
148
+ stackHash: stringHashCode(canonicalStackString),
169
149
  exceptionClass: stackInfo.name,
170
150
  request_uri: globalScope?.location.pathname
171
151
  };
@@ -203,10 +183,12 @@ export class Aggregate extends FeatureBase {
203
183
  time: time
204
184
  };
205
185
 
206
- // stn and spa aggregators listen to this event - stn sends the error in its payload,
186
+ // sr, stn and spa aggregators listen to this event - stn sends the error in its payload,
207
187
  // and spa annotates the error with interaction info
208
- handle('errorAgg', [type, bucketHash, params, newMetrics], undefined, FEATURE_NAMES.sessionTrace, this.ee);
209
- handle('errorAgg', [type, bucketHash, params, newMetrics], undefined, FEATURE_NAMES.spa, this.ee);
188
+ const msg = [type, bucketHash, params, newMetrics];
189
+ handle('errorAgg', msg, undefined, FEATURE_NAMES.sessionTrace, this.ee);
190
+ handle('errorAgg', msg, undefined, FEATURE_NAMES.spa, this.ee);
191
+ handle('errorAgg', msg, undefined, FEATURE_NAMES.sessionReplay, this.ee);
210
192
 
211
193
  // still send EE events for other features such as above, but stop this one from aggregating internal data
212
194
  if (this.blocked) return;
@@ -7,7 +7,6 @@ import { handle } from '../../../common/event-emitter/handle';
7
7
  import { now } from '../../../common/timing/now';
8
8
  import { getOrSet } from '../../../common/util/get-or-set';
9
9
  import { wrapRaf, wrapTimer, wrapEvents, wrapXhr } from '../../../common/wrap';
10
- import slice from 'lodash._slice';
11
10
  import './debug';
12
11
  import { InstrumentBase } from '../../utils/instrument-base';
13
12
  import { FEATURE_NAME, NR_ERR_PROP } from '../constants';
@@ -0,0 +1,61 @@
1
+ import { isBrowserScope } from '../../../common/util/global-scope';
2
+ const FRAMEWORKS = {
3
+ REACT: 'React',
4
+ ANGULAR: 'Angular',
5
+ ANGULARJS: 'AngularJS',
6
+ BACKBONE: 'Backbone',
7
+ EMBER: 'Ember',
8
+ VUE: 'Vue',
9
+ METEOR: 'Meteor',
10
+ ZEPTO: 'Zepto',
11
+ JQUERY: 'Jquery',
12
+ MOOTOOLS: 'MooTools'
13
+ };
14
+ export function getFrameworks() {
15
+ if (!isBrowserScope) return []; // don't bother detecting frameworks if not in the main window context
16
+
17
+ const frameworks = [];
18
+ try {
19
+ if (detectReact()) frameworks.push(FRAMEWORKS.REACT);
20
+ if (detectAngularJs()) frameworks.push(FRAMEWORKS.ANGULARJS);
21
+ if (detectAngular()) frameworks.push(FRAMEWORKS.ANGULAR);
22
+ if (Object.prototype.hasOwnProperty.call(window, 'Backbone')) frameworks.push(FRAMEWORKS.BACKBONE);
23
+ if (Object.prototype.hasOwnProperty.call(window, 'Ember')) frameworks.push(FRAMEWORKS.EMBER);
24
+ if (Object.prototype.hasOwnProperty.call(window, 'Vue')) frameworks.push(FRAMEWORKS.VUE);
25
+ if (Object.prototype.hasOwnProperty.call(window, 'Meteor')) frameworks.push(FRAMEWORKS.METEOR);
26
+ if (Object.prototype.hasOwnProperty.call(window, 'Zepto')) frameworks.push(FRAMEWORKS.ZEPTO);
27
+ if (Object.prototype.hasOwnProperty.call(window, 'jQuery')) frameworks.push(FRAMEWORKS.JQUERY);
28
+ if (Object.prototype.hasOwnProperty.call(window, 'MooTools')) frameworks.push(FRAMEWORKS.MOOTOOLS);
29
+ } catch (err) {
30
+ // Possibly not supported
31
+ }
32
+ return frameworks;
33
+ }
34
+ function detectReact() {
35
+ try {
36
+ return Object.prototype.hasOwnProperty.call(window, 'React') || Object.prototype.hasOwnProperty.call(window, 'ReactDOM') || Object.prototype.hasOwnProperty.call(window, 'ReactRedux') || document.querySelector('[data-reactroot], [data-reactid]') || (() => {
37
+ const divs = document.querySelectorAll('body > div');
38
+ for (let i = 0; i < divs.length; i++) {
39
+ if (Object.prototype.hasOwnProperty.call(divs[i], '_reactRootContainer')) {
40
+ return true;
41
+ }
42
+ }
43
+ })();
44
+ } catch (err) {
45
+ return false;
46
+ }
47
+ }
48
+ function detectAngularJs() {
49
+ try {
50
+ return Object.prototype.hasOwnProperty.call(window, 'angular') || document.querySelector('.ng-binding, [ng-app], [data-ng-app], [ng-controller], [data-ng-controller], [ng-repeat], [data-ng-repeat]') || document.querySelector('script[src*="angular.js"], script[src*="angular.min.js"]');
51
+ } catch (err) {
52
+ return false;
53
+ }
54
+ }
55
+ function detectAngular() {
56
+ try {
57
+ return Object.prototype.hasOwnProperty.call(window, 'ng') || document.querySelector('[ng-version]');
58
+ } catch (err) {
59
+ return false;
60
+ }
61
+ }
@@ -0,0 +1,133 @@
1
+ import { faker } from '@faker-js/faker';
2
+ import { getFrameworks } from './framework-detection';
3
+ jest.mock('../../../common/util/global-scope', () => ({
4
+ isBrowserScope: true
5
+ }));
6
+ afterEach(() => {
7
+ document.body.innerHTML = '';
8
+ });
9
+ test('framework detection should not happen in non-browser scope', async () => {
10
+ global.React = {};
11
+ jest.resetModules();
12
+ jest.doMock('../../../common/util/global-scope', () => ({
13
+ isBrowserScope: false
14
+ }));
15
+ const frameworkDetector = await import('./framework-detection');
16
+ expect(frameworkDetector.getFrameworks()).toEqual([]);
17
+ delete global.React;
18
+ });
19
+ test('should detect react from global React property', () => {
20
+ global.React = {};
21
+ expect(getFrameworks()).toEqual(['React']);
22
+ delete global.React;
23
+ });
24
+ test('should detect react from global ReactDOM property', () => {
25
+ global.ReactDOM = {};
26
+ expect(getFrameworks()).toEqual(['React']);
27
+ delete global.ReactDOM;
28
+ });
29
+ test('should detect react from global ReactRedux property', () => {
30
+ global.ReactRedux = {};
31
+ expect(getFrameworks()).toEqual(['React']);
32
+ delete global.ReactRedux;
33
+ });
34
+ test('should detect react from html [data-reactroot] property', () => {
35
+ document.body.innerHTML = '<div data-reactroot=""></div>';
36
+ expect(getFrameworks()).toEqual(['React']);
37
+ });
38
+ test('should detect react from html [data-reactid] property', () => {
39
+ document.body.innerHTML = '<div data-reactid=""></div>';
40
+ expect(getFrameworks()).toEqual(['React']);
41
+ });
42
+ test('should detect react from element _reactRootContainer property', () => {
43
+ const element = document.createElement('div');
44
+ element._reactRootContainer = {};
45
+ document.body.innerHTML = '<html><body></body></html>';
46
+ document.body.appendChild(element);
47
+ expect(getFrameworks()).toEqual(['React']);
48
+ });
49
+ test('should detect angularjs from global angular property', () => {
50
+ global.angular = {};
51
+ expect(getFrameworks()).toEqual(['AngularJS']);
52
+ delete global.angular;
53
+ });
54
+ test('should detect angularjs from html .ng-binding property', () => {
55
+ document.body.innerHTML = '<div class="ng-binding"></div>';
56
+ expect(getFrameworks()).toEqual(['AngularJS']);
57
+ });
58
+ test('should detect angularjs from html [ng-app] property', () => {
59
+ document.body.innerHTML = '<div ng-app=""></div>';
60
+ expect(getFrameworks()).toEqual(['AngularJS']);
61
+ });
62
+ test('should detect angularjs from html [data-ng-app] property', () => {
63
+ document.body.innerHTML = '<div data-ng-app=""></div>';
64
+ expect(getFrameworks()).toEqual(['AngularJS']);
65
+ });
66
+ test('should detect angularjs from html [ng-controller] property', () => {
67
+ document.body.innerHTML = '<div ng-controller=""></div>';
68
+ expect(getFrameworks()).toEqual(['AngularJS']);
69
+ });
70
+ test('should detect angularjs from html [data-ng-controller] property', () => {
71
+ document.body.innerHTML = '<div data-ng-controller=""></div>';
72
+ expect(getFrameworks()).toEqual(['AngularJS']);
73
+ });
74
+ test('should detect angularjs from html [ng-repeat] property', () => {
75
+ document.body.innerHTML = '<div ng-repeat=""></div>';
76
+ expect(getFrameworks()).toEqual(['AngularJS']);
77
+ });
78
+ test('should detect angularjs from html [data-ng-repeat] property', () => {
79
+ document.body.innerHTML = '<div data-ng-repeat=""></div>';
80
+ expect(getFrameworks()).toEqual(['AngularJS']);
81
+ });
82
+ test('should detect angularjs from angular.js script element', () => {
83
+ document.body.innerHTML = "<script src=\"".concat(faker.internet.url(), "/angular.js\"></script>");
84
+ expect(getFrameworks()).toEqual(['AngularJS']);
85
+ });
86
+ test('should detect angularjs from angular.min.js script element', () => {
87
+ document.body.innerHTML = "<script src=\"".concat(faker.internet.url(), "/angular.min.js\"></script>");
88
+ expect(getFrameworks()).toEqual(['AngularJS']);
89
+ });
90
+ test('should detect angular from global ng property', () => {
91
+ global.ng = {};
92
+ expect(getFrameworks()).toEqual(['Angular']);
93
+ delete global.ng;
94
+ });
95
+ test('should detect angular from html [ng-version] property', () => {
96
+ document.body.innerHTML = '<div ng-version=""></div>';
97
+ expect(getFrameworks()).toEqual(['Angular']);
98
+ });
99
+ test('should detect backbone from global Backbone property', () => {
100
+ global.Backbone = {};
101
+ expect(getFrameworks()).toEqual(['Backbone']);
102
+ delete global.Backbone;
103
+ });
104
+ test('should detect ember from global Ember property', () => {
105
+ global.Ember = {};
106
+ expect(getFrameworks()).toEqual(['Ember']);
107
+ delete global.Ember;
108
+ });
109
+ test('should detect vue from global Vue property', () => {
110
+ global.Vue = {};
111
+ expect(getFrameworks()).toEqual(['Vue']);
112
+ delete global.Vue;
113
+ });
114
+ test('should detect meteor from global Meteor property', () => {
115
+ global.Meteor = {};
116
+ expect(getFrameworks()).toEqual(['Meteor']);
117
+ delete global.Meteor;
118
+ });
119
+ test('should detect zepto from global Zepto property', () => {
120
+ global.Zepto = {};
121
+ expect(getFrameworks()).toEqual(['Zepto']);
122
+ delete global.Zepto;
123
+ });
124
+ test('should detect jquery from global jQuery property', () => {
125
+ global.jQuery = {};
126
+ expect(getFrameworks()).toEqual(['Jquery']);
127
+ delete global.jQuery;
128
+ });
129
+ test('should detect mootools from global MooTools property', () => {
130
+ global.MooTools = {};
131
+ expect(getFrameworks()).toEqual(['MooTools']);
132
+ delete global.MooTools;
133
+ });