@newrelic/browser-agent 1.239.1 → 1.240.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.
- package/README.md +4 -0
- package/dist/cjs/cdn/pro.js +3 -2
- package/dist/cjs/cdn/spa.js +4 -3
- package/dist/cjs/common/config/state/init.js +6 -0
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/constants/runtime.js +9 -5
- package/dist/cjs/common/harvest/harvest.js +5 -3
- package/dist/cjs/common/vitals/constants.js +17 -0
- package/dist/cjs/common/vitals/cumulative-layout-shift.js +27 -0
- package/dist/cjs/common/vitals/first-contentful-paint.js +49 -0
- package/dist/cjs/common/vitals/first-input-delay.js +32 -0
- package/dist/{esm/features/page_view_timing → cjs/common/vitals}/first-paint.js +19 -17
- package/dist/cjs/common/vitals/interaction-to-next-paint.js +29 -0
- package/dist/cjs/common/vitals/largest-contentful-paint.js +41 -0
- package/dist/cjs/common/vitals/long-task.js +64 -0
- package/dist/cjs/common/vitals/time-to-first-byte.js +36 -0
- package/dist/cjs/common/vitals/vital-metric.js +71 -0
- package/dist/cjs/features/ajax/aggregate/index.js +4 -1
- package/dist/cjs/features/metrics/aggregate/index.js +7 -0
- package/dist/cjs/features/page_view_event/aggregate/index.js +18 -40
- package/dist/cjs/features/page_view_event/constants.js +2 -8
- package/dist/cjs/features/page_view_event/instrument/index.js +0 -22
- package/dist/cjs/features/page_view_timing/aggregate/index.js +27 -138
- package/dist/cjs/features/page_view_timing/instrument/index.js +0 -3
- package/dist/cjs/features/session_trace/aggregate/index.js +13 -1
- package/dist/cjs/features/spa/aggregate/index.js +4 -3
- package/dist/cjs/loaders/agent.js +3 -0
- package/dist/cjs/loaders/api/api.js +2 -0
- package/dist/cjs/loaders/api/apiAsync.js +4 -2
- package/dist/cjs/loaders/configure/configure.js +13 -1
- package/dist/cjs/loaders/configure/public-path.js +13 -0
- package/dist/cjs/loaders/configure/public-path.npm.js +10 -0
- package/dist/esm/cdn/pro.js +2 -1
- package/dist/esm/cdn/spa.js +2 -1
- package/dist/esm/common/config/state/init.js +6 -0
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/constants/runtime.js +5 -3
- package/dist/esm/common/harvest/harvest.js +6 -4
- package/dist/esm/common/vitals/constants.js +10 -0
- package/dist/esm/common/vitals/cumulative-layout-shift.js +20 -0
- package/dist/esm/common/vitals/first-contentful-paint.js +41 -0
- package/dist/esm/common/vitals/first-input-delay.js +25 -0
- package/dist/{cjs/features/page_view_timing → esm/common/vitals}/first-paint.js +12 -24
- package/dist/esm/common/vitals/interaction-to-next-paint.js +22 -0
- package/dist/esm/common/vitals/largest-contentful-paint.js +34 -0
- package/dist/esm/common/vitals/long-task.js +57 -0
- package/dist/esm/common/vitals/time-to-first-byte.js +29 -0
- package/dist/esm/common/vitals/vital-metric.js +64 -0
- package/dist/esm/features/ajax/aggregate/index.js +4 -1
- package/dist/esm/features/metrics/aggregate/index.js +8 -1
- package/dist/esm/features/page_view_event/aggregate/index.js +20 -42
- package/dist/esm/features/page_view_event/constants.js +1 -4
- package/dist/esm/features/page_view_event/instrument/index.js +0 -22
- package/dist/esm/features/page_view_timing/aggregate/index.js +28 -139
- package/dist/esm/features/page_view_timing/instrument/index.js +0 -3
- package/dist/esm/features/session_trace/aggregate/index.js +13 -1
- package/dist/esm/features/spa/aggregate/index.js +4 -3
- package/dist/esm/loaders/agent.js +2 -0
- package/dist/esm/loaders/api/api.js +2 -0
- package/dist/esm/loaders/api/apiAsync.js +5 -3
- package/dist/esm/loaders/configure/configure.js +13 -1
- package/dist/esm/loaders/configure/public-path.js +6 -0
- package/dist/esm/loaders/configure/public-path.npm.js +3 -0
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/constants/runtime.d.ts +3 -1
- package/dist/types/common/constants/runtime.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest.d.ts +0 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/vitals/constants.d.ts +11 -0
- package/dist/types/common/vitals/constants.d.ts.map +1 -0
- package/dist/types/common/vitals/cumulative-layout-shift.d.ts +3 -0
- package/dist/types/common/vitals/cumulative-layout-shift.d.ts.map +1 -0
- package/dist/types/common/vitals/first-contentful-paint.d.ts +3 -0
- package/dist/types/common/vitals/first-contentful-paint.d.ts.map +1 -0
- package/dist/types/common/vitals/first-input-delay.d.ts +3 -0
- package/dist/types/common/vitals/first-input-delay.d.ts.map +1 -0
- package/dist/types/common/vitals/first-paint.d.ts +3 -0
- package/dist/types/common/vitals/first-paint.d.ts.map +1 -0
- package/dist/types/common/vitals/interaction-to-next-paint.d.ts +3 -0
- package/dist/types/common/vitals/interaction-to-next-paint.d.ts.map +1 -0
- package/dist/types/common/vitals/largest-contentful-paint.d.ts +3 -0
- package/dist/types/common/vitals/largest-contentful-paint.d.ts.map +1 -0
- package/dist/types/common/vitals/long-task.d.ts +3 -0
- package/dist/types/common/vitals/long-task.d.ts.map +1 -0
- package/dist/types/common/vitals/time-to-first-byte.d.ts +3 -0
- package/dist/types/common/vitals/time-to-first-byte.d.ts.map +1 -0
- package/dist/types/common/vitals/vital-metric.d.ts +18 -0
- package/dist/types/common/vitals/vital-metric.d.ts.map +1 -0
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts +3 -2
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/constants.d.ts +0 -3
- package/dist/types/features/page_view_event/constants.d.ts.map +1 -1
- package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts +1 -3
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +9 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/dist/types/loaders/configure/public-path.d.ts +2 -0
- package/dist/types/loaders/configure/public-path.d.ts.map +1 -0
- package/dist/types/loaders/configure/public-path.npm.d.ts +2 -0
- package/dist/types/loaders/configure/public-path.npm.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/cdn/pro.js +2 -0
- package/src/cdn/spa.js +2 -0
- package/src/common/config/state/init.js +4 -0
- package/src/common/constants/runtime.js +7 -3
- package/src/common/constants/runtime.test.js +8 -0
- package/src/common/harvest/harvest.js +6 -4
- package/src/common/harvest/harvest.test.js +17 -0
- package/src/common/vitals/__mocks__/web-vitals.js +19 -0
- package/src/common/vitals/constants.js +10 -0
- package/src/common/vitals/cumulative-layout-shift.js +13 -0
- package/src/common/vitals/cumulative-layout-shift.test.js +71 -0
- package/src/common/vitals/first-contentful-paint.js +31 -0
- package/src/common/vitals/first-contentful-paint.test.js +124 -0
- package/src/common/vitals/first-input-delay.js +20 -0
- package/src/common/vitals/first-input-delay.test.js +88 -0
- package/src/{features/page_view_timing → common/vitals}/first-paint.js +11 -17
- package/src/common/vitals/first-paint.test.js +127 -0
- package/src/common/vitals/interaction-to-next-paint.js +13 -0
- package/src/common/vitals/interaction-to-next-paint.test.js +74 -0
- package/src/common/vitals/largest-contentful-paint.js +29 -0
- package/src/common/vitals/largest-contentful-paint.test.js +94 -0
- package/src/common/vitals/long-task.js +52 -0
- package/src/common/vitals/long-task.test.js +122 -0
- package/src/common/vitals/time-to-first-byte.js +21 -0
- package/src/common/vitals/time-to-first-byte.test.js +147 -0
- package/src/common/vitals/vital-metric.js +60 -0
- package/src/common/vitals/vital-metric.test.js +171 -0
- package/src/features/ajax/aggregate/index.js +5 -1
- package/src/features/metrics/aggregate/index.js +6 -1
- package/src/features/page_view_event/aggregate/index.js +20 -43
- package/src/features/page_view_event/constants.js +0 -3
- package/src/features/page_view_event/instrument/index.js +0 -21
- package/src/features/page_view_timing/aggregate/index.component-test.js +86 -0
- package/src/features/page_view_timing/aggregate/index.js +24 -102
- package/src/features/page_view_timing/instrument/index.js +0 -3
- package/src/features/session_trace/aggregate/index.js +15 -1
- package/src/features/spa/aggregate/index.js +4 -3
- package/src/loaders/agent.js +2 -0
- package/src/loaders/api/api.js +2 -0
- package/src/loaders/api/apiAsync.js +5 -4
- package/src/loaders/configure/configure.js +15 -7
- package/src/loaders/configure/public-path.js +6 -0
- package/src/loaders/configure/public-path.npm.js +4 -0
- package/dist/cjs/common/metrics/paint-metrics.js +0 -13
- package/dist/cjs/features/page_view_timing/long-tasks.js +0 -75
- package/dist/esm/common/metrics/paint-metrics.js +0 -6
- package/dist/esm/features/page_view_timing/long-tasks.js +0 -69
- package/dist/types/common/metrics/paint-metrics.d.ts +0 -2
- package/dist/types/common/metrics/paint-metrics.d.ts.map +0 -1
- package/dist/types/features/page_view_timing/first-paint.d.ts +0 -2
- package/dist/types/features/page_view_timing/first-paint.d.ts.map +0 -1
- package/dist/types/features/page_view_timing/long-tasks.d.ts +0 -2
- package/dist/types/features/page_view_timing/long-tasks.d.ts.map +0 -1
- package/src/common/metrics/paint-metrics.js +0 -6
- package/src/features/page_view_timing/long-tasks.js +0 -60
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_trace/aggregate/index.js"],"names":[],"mappings":"AAiCA;IACE,2BAAiC;IAGjC,iEAyHC;IAvHC,kBAA+C;IAK/C,sBAAiD;IACjD,yBAAc;IACd,sBAAe;IACf,8BAAkB;IAClB,iCAAqB;IACrB,wBAA0G;IAC1G,wBAA4G;IAC5G;;4EAEwE;IACxE,kCAAyB;IAGzB,0CAAsC;IAuGxC,sEAcC;IA6CD,oDAOC;IAGD,oCAwBC;IAGD,uEAkBC;IAED,oDAKC;IAED,wBAwBC;IAED,uCAuBC;IAGD,gDASC;IAID,qCAkBC;IAGD,qEAUC;IAGD,mEAUC;IAGD,yBAeC;IAED;;;;OAIG;IACH,2BAHW,MAAM,GACJ,MAAM,CAsBlB;IAGD
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_trace/aggregate/index.js"],"names":[],"mappings":"AAiCA;IACE,2BAAiC;IAGjC,iEAyHC;IAvHC,kBAA+C;IAK/C,sBAAiD;IACjD,yBAAc;IACd,sBAAe;IACf,8BAAkB;IAClB,iCAAqB;IACrB,wBAA0G;IAC1G,wBAA4G;IAC5G;;4EAEwE;IACxE,kCAAyB;IAGzB,0CAAsC;IAuGxC,sEAcC;IA6CD,oDAOC;IAGD,oCAwBC;IAGD,uEAkBC;IAED,oDAKC;IAED,wBAwBC;IAED,uCAuBC;IAGD,gDASC;IAID,qCAkBC;IAGD,qEAUC;IAGD,mEAUC;IAGD,yBAeC;IAED;;;;OAIG;IACH,2BAHW,MAAM,GACJ,MAAM,CAsBlB;IAGD;;;;;;YA2BM;0GAC8F;;YAE9F;0FAC8E;;YAE9E,4IAA4I;;;;;;MAKjJ;IAED,mEA6BC;;CACF;8BAjf6B,4BAA4B;6BAH7B,2BAA2B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/index.js"],"names":[],"mappings":"AA2BA;IACE,2BAAiC;IACjC,mDA8rBC;IA3rBC;;;;;;;;;;;;;;;;MAgBC;IAED,uBAAsC;CA0qBzC;8BAzsB6B,4BAA4B;2BAJ/B,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent.js"],"names":[],"mappings":"AAmBA;;GAEG;AAEH;;;GAGG;AACH;IACE,oDAuBC;IAbC,oCAAsC;IACtC,yCAAiF;IACjF,yBAAkB;IAElB,sCAAsD;IAWxD;;;;;MAOC;IAED,yBAgCC;IAID;;;;;;;;;OASG;IACH,6BAPW;QAAC,MAAM,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAC,QASrF;IAED;;;;OAIG;IACH,0BAFW,MAAM,QAIhB;IAED;;;;OAIG;IACH,eAFa,mBAAmB,CAI/B;CACF;kCA7GY,OAAO,yBAAyB,EAAE,mBAAmB;0BAjBxC,cAAc;2BAQb,gCAAgC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/api.js"],"names":[],"mappings":"AAkBA,2CAoBC;AAED;;;;;IA0DE;;;;OAIG;qBAFQ,MAAM;IAWjB;;;;OAIG;iCAFQ,MAAM,GAAC,IAAI;;;;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/api.js"],"names":[],"mappings":"AAkBA,2CAoBC;AAED;;;;;IA0DE;;;;OAIG;qBAFQ,MAAM;IAWjB;;;;OAIG;iCAFQ,MAAM,GAAC,IAAI;;;;EA0FvB;AA3LD,0CAA0C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiAsync.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/apiAsync.js"],"names":[],"mappings":"AAUA,
|
|
1
|
+
{"version":3,"file":"apiAsync.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/apiAsync.js"],"names":[],"mappings":"AAUA,mDAkFC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/configure.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/configure.js"],"names":[],"mappings":"AASA;;;;;;;;;;EA6CC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-path.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/public-path.js"],"names":[],"mappings":"AAEO,mDAGN"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-path.npm.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/public-path.npm.js"],"names":[],"mappings":"AACO,2CAEN"}
|
package/package.json
CHANGED
package/src/cdn/pro.js
CHANGED
|
@@ -11,6 +11,7 @@ import { Instrument as InstrumentMetrics } from '../features/metrics/instrument'
|
|
|
11
11
|
import { Instrument as InstrumentErrors } from '../features/jserrors/instrument'
|
|
12
12
|
import { Instrument as InstrumentXhr } from '../features/ajax/instrument'
|
|
13
13
|
import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument'
|
|
14
|
+
import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument'
|
|
14
15
|
import { Instrument as InstrumentPageAction } from '../features/page_action/instrument'
|
|
15
16
|
|
|
16
17
|
new Agent({
|
|
@@ -18,6 +19,7 @@ new Agent({
|
|
|
18
19
|
InstrumentPageViewEvent,
|
|
19
20
|
InstrumentPageViewTiming,
|
|
20
21
|
InstrumentSessionTrace,
|
|
22
|
+
InstrumentSessionReplay,
|
|
21
23
|
InstrumentXhr,
|
|
22
24
|
InstrumentMetrics,
|
|
23
25
|
InstrumentPageAction,
|
package/src/cdn/spa.js
CHANGED
|
@@ -10,6 +10,7 @@ import { Instrument as InstrumentMetrics } from '../features/metrics/instrument'
|
|
|
10
10
|
import { Instrument as InstrumentErrors } from '../features/jserrors/instrument'
|
|
11
11
|
import { Instrument as InstrumentXhr } from '../features/ajax/instrument'
|
|
12
12
|
import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument'
|
|
13
|
+
import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument'
|
|
13
14
|
import { Instrument as InstrumentSpa } from '../features/spa/instrument'
|
|
14
15
|
import { Instrument as InstrumentPageAction } from '../features/page_action/instrument'
|
|
15
16
|
|
|
@@ -19,6 +20,7 @@ new Agent({
|
|
|
19
20
|
InstrumentPageViewEvent,
|
|
20
21
|
InstrumentPageViewTiming,
|
|
21
22
|
InstrumentSessionTrace,
|
|
23
|
+
InstrumentSessionReplay,
|
|
22
24
|
InstrumentMetrics,
|
|
23
25
|
InstrumentPageAction,
|
|
24
26
|
InstrumentErrors,
|
|
@@ -8,6 +8,10 @@ const model = () => {
|
|
|
8
8
|
maskInputOptions: { password: true }
|
|
9
9
|
}
|
|
10
10
|
return {
|
|
11
|
+
proxy: {
|
|
12
|
+
assets: undefined, // if this value is set, it will be used to overwrite the webpack asset path used to fetch assets
|
|
13
|
+
beacon: undefined // likewise for the url to which we send analytics
|
|
14
|
+
},
|
|
11
15
|
privacy: { cookies_enabled: true }, // *cli - per discussion, default should be true
|
|
12
16
|
ajax: { deny_list: undefined, block_internal: true, enabled: true, harvestTimeSeconds: 10, autoStart: true },
|
|
13
17
|
distributed_tracing: {
|
|
@@ -44,9 +44,11 @@ export const globalScope = isBrowserScope
|
|
|
44
44
|
globalThis
|
|
45
45
|
))
|
|
46
46
|
|
|
47
|
+
export const initiallyHidden = Boolean(globalScope?.document?.visibilityState === 'hidden')
|
|
48
|
+
|
|
47
49
|
export const initialLocation = '' + globalScope?.location
|
|
48
50
|
|
|
49
|
-
export const isiOS = /iPad|iPhone|iPod/.test(navigator
|
|
51
|
+
export const isiOS = /iPad|iPhone|iPod/.test(globalScope.navigator?.userAgent)
|
|
50
52
|
|
|
51
53
|
/**
|
|
52
54
|
* Shared Web Workers introduced in iOS 16.0+ and n/a in 15.6-
|
|
@@ -58,7 +60,7 @@ export const isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent)
|
|
|
58
60
|
export const iOSBelow16 = (isiOS && typeof SharedWorker === 'undefined')
|
|
59
61
|
|
|
60
62
|
export const ffVersion = (() => {
|
|
61
|
-
const match = navigator
|
|
63
|
+
const match = globalScope.navigator?.userAgent?.match(/Firefox[/\s](\d+\.\d+)/)
|
|
62
64
|
if (Array.isArray(match) && match.length >= 2) {
|
|
63
65
|
return +match[1]
|
|
64
66
|
}
|
|
@@ -68,4 +70,6 @@ export const ffVersion = (() => {
|
|
|
68
70
|
|
|
69
71
|
export const isIE = Boolean(isBrowserScope && window.document.documentMode) // deprecated property that only works in IE
|
|
70
72
|
|
|
71
|
-
export const supportsSendBeacon = !!navigator
|
|
73
|
+
export const supportsSendBeacon = !!globalScope.navigator?.sendBeacon
|
|
74
|
+
|
|
75
|
+
export const offset = Math.floor(globalScope?.performance?.timeOrigin || globalScope?.performance?.timing?.navigationStart || Date.now())
|
|
@@ -106,10 +106,12 @@ test.each([
|
|
|
106
106
|
{ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 13.4; rv:109.0) Gecko/20100101 Firefox/114.0', expected: false }
|
|
107
107
|
])('should set isiOS to $expected for $userAgent', async ({ userAgent, expected }) => {
|
|
108
108
|
global.navigator.userAgent = userAgent
|
|
109
|
+
global.window = { navigator: global.navigator, document: true }
|
|
109
110
|
|
|
110
111
|
const runtime = await import('./runtime')
|
|
111
112
|
|
|
112
113
|
expect(runtime.isiOS).toEqual(expected)
|
|
114
|
+
delete global.window
|
|
113
115
|
})
|
|
114
116
|
|
|
115
117
|
test.each([
|
|
@@ -127,12 +129,14 @@ test.each([
|
|
|
127
129
|
global.SharedWorker = class SharedWorker {}
|
|
128
130
|
}
|
|
129
131
|
global.navigator.userAgent = userAgent
|
|
132
|
+
global.window = { navigator: global.navigator, document: true }
|
|
130
133
|
|
|
131
134
|
const runtime = await import('./runtime')
|
|
132
135
|
|
|
133
136
|
delete global.SharedWorker
|
|
134
137
|
|
|
135
138
|
expect(runtime.iOSBelow16).toEqual(expected)
|
|
139
|
+
delete global.window
|
|
136
140
|
})
|
|
137
141
|
|
|
138
142
|
test.each([
|
|
@@ -144,10 +148,12 @@ test.each([
|
|
|
144
148
|
{ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 13.4; rv:109.0) Gecko/20100101 Firefox/114.0', expected: 114 }
|
|
145
149
|
])('should set ffVersion to $expected for $userAgent', async ({ userAgent, expected }) => {
|
|
146
150
|
global.navigator.userAgent = userAgent
|
|
151
|
+
global.window = { navigator: global.navigator, document: true }
|
|
147
152
|
|
|
148
153
|
const runtime = await import('./runtime')
|
|
149
154
|
|
|
150
155
|
expect(runtime.ffVersion).toEqual(expected)
|
|
156
|
+
delete global.window
|
|
151
157
|
})
|
|
152
158
|
|
|
153
159
|
test('should set supportsSendBeacon to false', async () => {
|
|
@@ -161,8 +167,10 @@ test('should set supportsSendBeacon to false', async () => {
|
|
|
161
167
|
|
|
162
168
|
test('should set supportsSendBeacon to true', async () => {
|
|
163
169
|
global.navigator.sendBeacon = jest.fn()
|
|
170
|
+
global.window = { navigator: global.navigator, document: true }
|
|
164
171
|
|
|
165
172
|
const runtime = await import('./runtime')
|
|
166
173
|
|
|
167
174
|
expect(runtime.supportsSendBeacon).toEqual(true)
|
|
175
|
+
delete global.window
|
|
168
176
|
})
|
|
@@ -7,7 +7,7 @@ import { obj as encodeObj, param as encodeParam } from '../url/encode'
|
|
|
7
7
|
import { stringify } from '../util/stringify'
|
|
8
8
|
import * as submitData from '../util/submit-data'
|
|
9
9
|
import { getLocation } from '../url/location'
|
|
10
|
-
import { getInfo, getConfigurationValue, getRuntime } from '../config/config'
|
|
10
|
+
import { getInfo, getConfigurationValue, getRuntime, getConfiguration } from '../config/config'
|
|
11
11
|
import { cleanURL } from '../url/clean-url'
|
|
12
12
|
import { now } from '../timing/now'
|
|
13
13
|
import { eventListenerOpts } from '../event-listener/event-listener-opts'
|
|
@@ -33,7 +33,6 @@ export class Harvest extends SharedContext {
|
|
|
33
33
|
|
|
34
34
|
this.tooManyRequestsDelay = getConfigurationValue(this.sharedContext.agentIdentifier, 'harvest.tooManyRequestsDelay') || 60
|
|
35
35
|
this.obfuscator = new Obfuscator(this.sharedContext)
|
|
36
|
-
this.getScheme = () => (getConfigurationValue(this.sharedContext.agentIdentifier, 'ssl') === false) ? 'http' : 'https'
|
|
37
36
|
|
|
38
37
|
this._events = {}
|
|
39
38
|
}
|
|
@@ -96,10 +95,13 @@ export class Harvest extends SharedContext {
|
|
|
96
95
|
return false
|
|
97
96
|
}
|
|
98
97
|
|
|
98
|
+
const init = getConfiguration(this.sharedContext.agentIdentifier)
|
|
99
|
+
const protocol = init.ssl === false ? 'http' : 'https'
|
|
100
|
+
const perceviedBeacon = init.proxy.beacon || info.errorBeacon
|
|
99
101
|
const endpointURLPart = endpoint !== 'rum' ? `/${endpoint}` : ''
|
|
100
|
-
let url = `${
|
|
102
|
+
let url = `${protocol}://${perceviedBeacon}${endpointURLPart}/1/${info.licenseKey}`
|
|
101
103
|
if (customUrl) url = customUrl
|
|
102
|
-
if (raw) url = `${
|
|
104
|
+
if (raw) url = `${protocol}://${perceviedBeacon}/${endpoint}`
|
|
103
105
|
|
|
104
106
|
const baseParams = !raw && includeBaseParams ? this.baseQueryString() : ''
|
|
105
107
|
let payloadParams = encodeObj(qs, agentRuntime.maxBytes)
|
|
@@ -159,6 +159,10 @@ describe('_send', () => {
|
|
|
159
159
|
jest.mocked(configModule.getRuntime).mockReturnValue({
|
|
160
160
|
maxBytes: Infinity
|
|
161
161
|
})
|
|
162
|
+
jest.mocked(configModule.getConfiguration).mockReturnValue({
|
|
163
|
+
ssl: undefined,
|
|
164
|
+
proxy: {}
|
|
165
|
+
})
|
|
162
166
|
|
|
163
167
|
spec = {
|
|
164
168
|
endpoint: faker.datatype.uuid(),
|
|
@@ -223,6 +227,19 @@ describe('_send', () => {
|
|
|
223
227
|
})
|
|
224
228
|
})
|
|
225
229
|
|
|
230
|
+
test('able to use and send to proxy when defined', () => {
|
|
231
|
+
jest.mocked(configModule.getConfiguration).mockReturnValue({ proxy: { beacon: 'some_other_string' } })
|
|
232
|
+
const result = harvestInstance._send(spec)
|
|
233
|
+
|
|
234
|
+
expect(result).toEqual(true)
|
|
235
|
+
expect(submitMethod).toHaveBeenCalledWith({
|
|
236
|
+
body: JSON.stringify(spec.payload.body),
|
|
237
|
+
headers: [{ key: 'content-type', value: 'text/plain' }],
|
|
238
|
+
sync: undefined,
|
|
239
|
+
url: expect.stringContaining(`https://some_other_string/${spec.endpoint}/1/${licenseKey}?`)
|
|
240
|
+
})
|
|
241
|
+
})
|
|
242
|
+
|
|
226
243
|
test('should use the custom defined url', () => {
|
|
227
244
|
spec.customUrl = faker.internet.url()
|
|
228
245
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const continuouslyReportMetric = c => {
|
|
2
|
+
let count = 0
|
|
3
|
+
const vital = { value: 1, entries: [{ startTime: 1, name: 'name', size: 1, id: `id${++count}`, url: 'url', element: { tagName: 'tagName' } }], id: 'id' }
|
|
4
|
+
// report a new metric every quarter second
|
|
5
|
+
const callcb = () => {
|
|
6
|
+
setTimeout(() => {
|
|
7
|
+
c(vital)
|
|
8
|
+
callcb()
|
|
9
|
+
}, 250)
|
|
10
|
+
}
|
|
11
|
+
callcb()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const onCLS = c => continuouslyReportMetric(c)
|
|
15
|
+
export const onFCP = c => continuouslyReportMetric(c)
|
|
16
|
+
export const onFID = c => continuouslyReportMetric(c)
|
|
17
|
+
export const onINP = c => continuouslyReportMetric(c)
|
|
18
|
+
export const onLCP = c => continuouslyReportMetric(c)
|
|
19
|
+
export const onTTFB = c => continuouslyReportMetric(c)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const VITAL_NAMES = {
|
|
2
|
+
FIRST_PAINT: 'fp',
|
|
3
|
+
FIRST_CONTENTFUL_PAINT: 'fcp',
|
|
4
|
+
FIRST_INPUT_DELAY: 'fi',
|
|
5
|
+
LARGEST_CONTENTFUL_PAINT: 'lcp',
|
|
6
|
+
CUMULATIVE_LAYOUT_SHIFT: 'cls',
|
|
7
|
+
INTERACTION_TO_NEXT_PAINT: 'inp',
|
|
8
|
+
LONG_TASK: 'lt',
|
|
9
|
+
TIME_TO_FIRST_BYTE: 'ttfb'
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { onCLS } from 'web-vitals'
|
|
2
|
+
import { VITAL_NAMES } from './constants'
|
|
3
|
+
import { VitalMetric } from './vital-metric'
|
|
4
|
+
import { isBrowserScope } from '../constants/runtime'
|
|
5
|
+
|
|
6
|
+
export const cumulativeLayoutShift = new VitalMetric(VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT, (x) => x)
|
|
7
|
+
|
|
8
|
+
if (isBrowserScope) {
|
|
9
|
+
onCLS(({ value, entries }) => {
|
|
10
|
+
if (cumulativeLayoutShift.roundingMethod(value) === cumulativeLayoutShift.current.value) return
|
|
11
|
+
cumulativeLayoutShift.update({ value, entries })
|
|
12
|
+
}, { reportAllChanges: true })
|
|
13
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
afterEach(() => {
|
|
2
|
+
jest.resetModules()
|
|
3
|
+
jest.resetAllMocks()
|
|
4
|
+
jest.clearAllMocks()
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
const getFreshCLSImport = async (codeToRun) => {
|
|
8
|
+
const { cumulativeLayoutShift } = await import('./cumulative-layout-shift')
|
|
9
|
+
codeToRun(cumulativeLayoutShift)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('cls', () => {
|
|
13
|
+
test('reports cls', (done) => {
|
|
14
|
+
getFreshCLSImport(metric => {
|
|
15
|
+
metric.subscribe(({ value }) => {
|
|
16
|
+
expect(value).toEqual(1)
|
|
17
|
+
done()
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
test('does NOT report if not browser scoped', (done) => {
|
|
22
|
+
jest.doMock('../constants/runtime', () => ({
|
|
23
|
+
__esModule: true,
|
|
24
|
+
isBrowserScope: false
|
|
25
|
+
}))
|
|
26
|
+
|
|
27
|
+
getFreshCLSImport(metric => {
|
|
28
|
+
metric.subscribe(() => {
|
|
29
|
+
console.log('should not have reported...')
|
|
30
|
+
expect(1).toEqual(2)
|
|
31
|
+
})
|
|
32
|
+
setTimeout(done, 1000)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
test('multiple subs get same value', done => {
|
|
36
|
+
jest.doMock('../constants/runtime', () => ({
|
|
37
|
+
__esModule: true,
|
|
38
|
+
isBrowserScope: true
|
|
39
|
+
}))
|
|
40
|
+
let sub1, sub2
|
|
41
|
+
getFreshCLSImport(metric => {
|
|
42
|
+
const remove1 = metric.subscribe(({ entries }) => {
|
|
43
|
+
sub1 ??= entries[0].id
|
|
44
|
+
if (sub1 === sub2) { remove1(); remove2(); done() }
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const remove2 = metric.subscribe(({ entries }) => {
|
|
48
|
+
sub2 ??= entries[0].id
|
|
49
|
+
if (sub1 === sub2) { remove1(); remove2(); done() }
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
test('reports only new values', (done) => {
|
|
54
|
+
jest.doMock('../constants/runtime', () => ({
|
|
55
|
+
__esModule: true,
|
|
56
|
+
isBrowserScope: true
|
|
57
|
+
}))
|
|
58
|
+
let triggered = 0
|
|
59
|
+
getFreshCLSImport(metric => {
|
|
60
|
+
metric.subscribe(({ value }) => {
|
|
61
|
+
triggered++
|
|
62
|
+
expect(value).toEqual(1)
|
|
63
|
+
expect(triggered).toEqual(1)
|
|
64
|
+
setTimeout(() => {
|
|
65
|
+
expect(triggered).toEqual(1)
|
|
66
|
+
done()
|
|
67
|
+
}, 1000)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { onFCP } from 'web-vitals'
|
|
2
|
+
// eslint-disable-next-line camelcase
|
|
3
|
+
import { iOSBelow16, initiallyHidden, isBrowserScope } from '../constants/runtime'
|
|
4
|
+
import { VITAL_NAMES } from './constants'
|
|
5
|
+
import { VitalMetric } from './vital-metric'
|
|
6
|
+
|
|
7
|
+
export const firstContentfulPaint = new VitalMetric(VITAL_NAMES.FIRST_CONTENTFUL_PAINT)
|
|
8
|
+
|
|
9
|
+
/* First Contentful Paint - As of WV v3, it still imperfectly tries to detect document vis state asap and isn't supposed to report if page starts hidden. */
|
|
10
|
+
if (isBrowserScope) {
|
|
11
|
+
// eslint-disable-next-line camelcase
|
|
12
|
+
if (iOSBelow16) {
|
|
13
|
+
try {
|
|
14
|
+
if (!initiallyHidden) { // see ios-version.js for detail on this following bug case; tldr: buffered flag doesn't work but getEntriesByType does
|
|
15
|
+
const paintEntries = performance.getEntriesByType('paint')
|
|
16
|
+
paintEntries.forEach(entry => {
|
|
17
|
+
if (entry.name === 'first-contentful-paint') {
|
|
18
|
+
firstContentfulPaint.update({ value: Math.floor(entry.startTime), entries: paintEntries })
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
} catch (e) {
|
|
23
|
+
// ignore
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
onFCP(({ value, entries }) => {
|
|
27
|
+
if (initiallyHidden || firstContentfulPaint.isValid) return
|
|
28
|
+
firstContentfulPaint.update({ value, entries })
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
beforeEach(() => {
|
|
2
|
+
jest.resetModules()
|
|
3
|
+
jest.resetAllMocks()
|
|
4
|
+
jest.clearAllMocks()
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
const getFreshFCPImport = async (codeToRun) => {
|
|
8
|
+
const { firstContentfulPaint } = await import('./first-contentful-paint')
|
|
9
|
+
codeToRun(firstContentfulPaint)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('fcp', () => {
|
|
13
|
+
test('reports fcp from web-vitals', (done) => {
|
|
14
|
+
getFreshFCPImport(firstContentfulPaint => firstContentfulPaint.subscribe(({ value }) => {
|
|
15
|
+
expect(value).toEqual(1)
|
|
16
|
+
done()
|
|
17
|
+
}))
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('reports fcp from paintEntries if ios<16', (done) => {
|
|
21
|
+
jest.doMock('../constants/runtime', () => ({
|
|
22
|
+
__esModule: true,
|
|
23
|
+
iOSBelow16: true,
|
|
24
|
+
initiallyHidden: false,
|
|
25
|
+
isBrowserScope: true
|
|
26
|
+
}))
|
|
27
|
+
global.performance.getEntriesByType = jest.fn(() => [{ name: 'first-contentful-paint', startTime: 1 }])
|
|
28
|
+
|
|
29
|
+
getFreshFCPImport(firstContentfulPaint => firstContentfulPaint.subscribe(({ value }) => {
|
|
30
|
+
expect(value).toEqual(1)
|
|
31
|
+
done()
|
|
32
|
+
}))
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('does NOT report if not browser scoped', (done) => {
|
|
36
|
+
jest.doMock('../constants/runtime', () => ({
|
|
37
|
+
__esModule: true,
|
|
38
|
+
isBrowserScope: false
|
|
39
|
+
}))
|
|
40
|
+
|
|
41
|
+
getFreshFCPImport(metric => {
|
|
42
|
+
metric.subscribe(({ value, attrs }) => {
|
|
43
|
+
console.log('should not have reported...')
|
|
44
|
+
expect(1).toEqual(2)
|
|
45
|
+
})
|
|
46
|
+
setTimeout(done, 1000)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('Does NOT report values from paintEntries other than fcp', (done) => {
|
|
51
|
+
jest.doMock('../constants/runtime', () => ({
|
|
52
|
+
__esModule: true,
|
|
53
|
+
iOSBelow16: true,
|
|
54
|
+
initiallyHidden: false,
|
|
55
|
+
isBrowserScope: true
|
|
56
|
+
}))
|
|
57
|
+
global.performance.getEntriesByType = jest.fn(() => [{ name: 'other-timing-name', startTime: 1 }])
|
|
58
|
+
|
|
59
|
+
getFreshFCPImport(firstContentfulPaint => {
|
|
60
|
+
firstContentfulPaint.subscribe(() => {
|
|
61
|
+
console.log('should not have reported')
|
|
62
|
+
expect(1).toEqual(2)
|
|
63
|
+
})
|
|
64
|
+
setTimeout(done, 1000)
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('Does NOT report fcp from paintEntries if ios<16 && initiallyHidden', (done) => {
|
|
69
|
+
jest.doMock('../constants/runtime', () => ({
|
|
70
|
+
__esModule: true,
|
|
71
|
+
iOSBelow16: true,
|
|
72
|
+
initiallyHidden: true,
|
|
73
|
+
isBrowserScope: true
|
|
74
|
+
}))
|
|
75
|
+
global.performance.getEntriesByType = jest.fn(() => [{ name: 'first-contentful-paint', startTime: 1 }])
|
|
76
|
+
|
|
77
|
+
getFreshFCPImport(firstContentfulPaint => {
|
|
78
|
+
firstContentfulPaint.subscribe(() => {
|
|
79
|
+
console.log('should not have reported....')
|
|
80
|
+
expect(1).toEqual(2)
|
|
81
|
+
})
|
|
82
|
+
setTimeout(done, 2000)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('multiple subs get same value', done => {
|
|
87
|
+
jest.doMock('../constants/runtime', () => ({
|
|
88
|
+
__esModule: true,
|
|
89
|
+
isBrowserScope: true
|
|
90
|
+
}))
|
|
91
|
+
let sub1, sub2
|
|
92
|
+
getFreshFCPImport(metric => {
|
|
93
|
+
const remove1 = metric.subscribe(({ entries }) => {
|
|
94
|
+
sub1 ??= entries[0].id
|
|
95
|
+
if (sub1 === sub2) { remove1(); remove2(); done() }
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const remove2 = metric.subscribe(({ entries }) => {
|
|
99
|
+
sub2 ??= entries[0].id
|
|
100
|
+
if (sub1 === sub2) { remove1(); remove2(); done() }
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test('reports only once', (done) => {
|
|
106
|
+
jest.doMock('../constants/runtime', () => ({
|
|
107
|
+
__esModule: true,
|
|
108
|
+
iOSBelow16: false,
|
|
109
|
+
initiallyHidden: false,
|
|
110
|
+
isBrowserScope: true
|
|
111
|
+
}))
|
|
112
|
+
let triggered = 0
|
|
113
|
+
getFreshFCPImport(firstContentfulPaint => firstContentfulPaint.subscribe(({ value }) => {
|
|
114
|
+
triggered++
|
|
115
|
+
expect(value).toEqual(1)
|
|
116
|
+
expect(triggered).toEqual(1)
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
expect(triggered).toEqual(1)
|
|
119
|
+
done()
|
|
120
|
+
}, 1000)
|
|
121
|
+
})
|
|
122
|
+
)
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { onFID } from 'web-vitals'
|
|
2
|
+
import { VitalMetric } from './vital-metric'
|
|
3
|
+
import { VITAL_NAMES } from './constants'
|
|
4
|
+
import { initiallyHidden, isBrowserScope } from '../constants/runtime'
|
|
5
|
+
|
|
6
|
+
export const firstInputDelay = new VitalMetric(VITAL_NAMES.FIRST_INPUT_DELAY)
|
|
7
|
+
|
|
8
|
+
if (isBrowserScope) {
|
|
9
|
+
onFID(({ value, entries }) => {
|
|
10
|
+
if (initiallyHidden || firstInputDelay.isValid || entries.length === 0) return
|
|
11
|
+
|
|
12
|
+
// CWV will only report one (THE) first-input entry to us; fid isn't reported if there are no user interactions occurs before the *first* page hiding.
|
|
13
|
+
firstInputDelay.update({
|
|
14
|
+
value: entries[0].startTime,
|
|
15
|
+
entries,
|
|
16
|
+
attrs: { type: entries[0].name, fid: Math.round(value) },
|
|
17
|
+
shouldAddConnectionAttributes: true
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
beforeEach(() => {
|
|
2
|
+
jest.resetModules()
|
|
3
|
+
jest.resetAllMocks()
|
|
4
|
+
jest.clearAllMocks()
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
const getFreshFIDImport = async (codeToRun) => {
|
|
8
|
+
const { firstInputDelay } = await import('./first-input-delay')
|
|
9
|
+
codeToRun(firstInputDelay)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('fid', () => {
|
|
13
|
+
test('reports fcp from web-vitals', (done) => {
|
|
14
|
+
getFreshFIDImport(metric => metric.subscribe(({ value }) => {
|
|
15
|
+
expect(value).toEqual(1)
|
|
16
|
+
done()
|
|
17
|
+
}))
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('Does NOT report values if initiallyHidden', (done) => {
|
|
21
|
+
jest.doMock('../constants/runtime', () => ({
|
|
22
|
+
__esModule: true,
|
|
23
|
+
initiallyHidden: true,
|
|
24
|
+
isBrowserScope: true
|
|
25
|
+
}))
|
|
26
|
+
|
|
27
|
+
getFreshFIDImport(metric => {
|
|
28
|
+
metric.subscribe(() => {
|
|
29
|
+
console.log('should not have reported')
|
|
30
|
+
expect(1).toEqual(2)
|
|
31
|
+
})
|
|
32
|
+
setTimeout(done, 1000)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('does NOT report if not browser scoped', (done) => {
|
|
37
|
+
jest.doMock('../constants/runtime', () => ({
|
|
38
|
+
__esModule: true,
|
|
39
|
+
isBrowserScope: false
|
|
40
|
+
}))
|
|
41
|
+
|
|
42
|
+
getFreshFIDImport(metric => {
|
|
43
|
+
metric.subscribe(() => {
|
|
44
|
+
console.log('should not have reported...')
|
|
45
|
+
expect(1).toEqual(2)
|
|
46
|
+
})
|
|
47
|
+
setTimeout(done, 1000)
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('multiple subs get same value', done => {
|
|
52
|
+
jest.doMock('../constants/runtime', () => ({
|
|
53
|
+
__esModule: true,
|
|
54
|
+
isBrowserScope: true
|
|
55
|
+
}))
|
|
56
|
+
let sub1, sub2
|
|
57
|
+
getFreshFIDImport(metric => {
|
|
58
|
+
const remove1 = metric.subscribe(({ entries }) => {
|
|
59
|
+
sub1 ??= entries[0].id
|
|
60
|
+
if (sub1 === sub2) { remove1(); remove2(); done() }
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const remove2 = metric.subscribe(({ entries }) => {
|
|
64
|
+
sub2 ??= entries[0].id
|
|
65
|
+
if (sub1 === sub2) { remove1(); remove2(); done() }
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('reports only once', (done) => {
|
|
71
|
+
jest.doMock('../constants/runtime', () => ({
|
|
72
|
+
__esModule: true,
|
|
73
|
+
initiallyHidden: false,
|
|
74
|
+
isBrowserScope: true
|
|
75
|
+
}))
|
|
76
|
+
let triggered = 0
|
|
77
|
+
getFreshFIDImport(metric => metric.subscribe(({ value }) => {
|
|
78
|
+
triggered++
|
|
79
|
+
expect(value).toEqual(1)
|
|
80
|
+
expect(triggered).toEqual(1)
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
expect(triggered).toEqual(1)
|
|
83
|
+
done()
|
|
84
|
+
}, 1000)
|
|
85
|
+
})
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
})
|