@newrelic/browser-agent 1.239.1 → 1.241.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 +25 -17
- 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 +17 -7
- 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 +36 -147
- package/dist/cjs/features/page_view_timing/instrument/index.js +0 -3
- package/dist/cjs/features/session_replay/aggregate/index.js +81 -35
- 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/browser-agent.js +2 -1
- 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 +25 -17
- 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 +18 -8
- 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 +37 -148
- package/dist/esm/features/page_view_timing/instrument/index.js +0 -3
- package/dist/esm/features/session_replay/aggregate/index.js +81 -35
- 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/browser-agent.js +2 -1
- 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_replay/aggregate/index.d.ts +16 -4
- package/dist/types/features/session_replay/aggregate/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/browser-agent.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 +2 -3
- package/src/cdn/pro.js +2 -0
- package/src/cdn/spa.js +2 -0
- package/src/common/config/state/init.js +21 -17
- package/src/common/constants/runtime.js +7 -3
- package/src/common/constants/runtime.test.js +8 -0
- package/src/common/harvest/harvest-scheduler.test.js +2 -2
- 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 +11 -4
- 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 +31 -108
- package/src/features/page_view_timing/instrument/index.js +0 -3
- package/src/features/session_replay/aggregate/index.component-test.js +10 -10
- package/src/features/session_replay/aggregate/index.js +62 -29
- 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/browser-agent.js +3 -1
- 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
|
@@ -67,6 +67,9 @@ export class Aggregate extends AggregateBase {
|
|
|
67
67
|
|
|
68
68
|
this.drain()
|
|
69
69
|
|
|
70
|
+
const beacon = getInfo(agentIdentifier).errorBeacon
|
|
71
|
+
const proxyBeacon = agentInit.proxy.beacon
|
|
72
|
+
|
|
70
73
|
function storeXhr (params, metrics, startTime, endTime, type) {
|
|
71
74
|
metrics.time = startTime
|
|
72
75
|
|
|
@@ -86,7 +89,8 @@ export class Aggregate extends AggregateBase {
|
|
|
86
89
|
if (!allAjaxIsEnabled) return
|
|
87
90
|
|
|
88
91
|
if (!shouldCollectEvent(params)) {
|
|
89
|
-
if (params.hostname ===
|
|
92
|
+
if (params.hostname === beacon || (proxyBeacon && params.hostname === proxyBeacon)) {
|
|
93
|
+
// This doesn't make a distinction if the same-domain request is going to a different port or path...
|
|
90
94
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/Agent'], undefined, FEATURE_NAMES.metrics, ee)
|
|
91
95
|
} else {
|
|
92
96
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/App'], undefined, FEATURE_NAMES.metrics, ee)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getRuntime } from '../../../common/config/config'
|
|
1
|
+
import { getRuntime, getConfiguration } from '../../../common/config/config'
|
|
2
2
|
import { registerHandler } from '../../../common/event-emitter/register-handler'
|
|
3
3
|
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
|
|
4
4
|
import { FEATURE_NAME, SUPPORTABILITY_METRIC, CUSTOM_METRIC, SUPPORTABILITY_METRIC_CHANNEL, CUSTOM_METRIC_CHANNEL } from '../constants'
|
|
@@ -29,9 +29,11 @@ export class Aggregate extends AggregateBase {
|
|
|
29
29
|
this.singleChecks() // checks that are run only one time, at script load
|
|
30
30
|
this.eachSessionChecks() // the start of every time user engages with page
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
this.ee.on(`drain-${this.featureName}`, () => {
|
|
33
|
+
// *cli, Mar 23 - Per NR-94597, this feature should only harvest ONCE at the (potential) EoL time of the page.
|
|
34
|
+
scheduler = new HarvestScheduler('jserrors', { onUnload: () => this.unload() }, this)
|
|
35
|
+
scheduler.harvest.on('jserrors', () => ({ body: this.aggregator.take(['cm', 'sm']) }))
|
|
36
|
+
}) // this is needed to ensure EoL is "on" and sent
|
|
35
37
|
|
|
36
38
|
this.drain()
|
|
37
39
|
}
|
|
@@ -82,6 +84,11 @@ export class Aggregate extends AggregateBase {
|
|
|
82
84
|
const rules = getRules(this.agentIdentifier)
|
|
83
85
|
if (rules.length > 0) this.storeSupportabilityMetrics('Generic/Obfuscate/Detected')
|
|
84
86
|
if (rules.length > 0 && !validateRules(rules)) this.storeSupportabilityMetrics('Generic/Obfuscate/Invalid')
|
|
87
|
+
|
|
88
|
+
// Check if proxy for either chunks or beacon is being used
|
|
89
|
+
const { proxy } = getConfiguration(this.agentIdentifier)
|
|
90
|
+
if (proxy.assets) this.storeSupportabilityMetrics('Config/AssetsUrl/Changed')
|
|
91
|
+
if (proxy.beacon) this.storeSupportabilityMetrics('Config/BeaconUrl/Changed')
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
eachSessionChecks () {
|
|
@@ -1,53 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
3
|
-
import { isiOS, globalScope, isBrowserScope } from '../../../common/constants/runtime'
|
|
4
|
-
import { onTTFB } from 'web-vitals'
|
|
1
|
+
import { globalScope, isBrowserScope } from '../../../common/constants/runtime'
|
|
5
2
|
import { addPT, addPN } from '../../../common/timing/nav-timing'
|
|
6
3
|
import { stringify } from '../../../common/util/stringify'
|
|
7
|
-
import {
|
|
8
|
-
import { getConfigurationValue, getInfo, getRuntime } from '../../../common/config/config'
|
|
4
|
+
import { getInfo, getRuntime } from '../../../common/config/config'
|
|
9
5
|
import { Harvest } from '../../../common/harvest/harvest'
|
|
10
6
|
import * as CONSTANTS from '../constants'
|
|
11
7
|
import { getActivatedFeaturesFlags } from './initialized-features'
|
|
12
8
|
import { activateFeatures } from '../../../common/util/feature-flags'
|
|
13
9
|
import { warn } from '../../../common/util/console'
|
|
14
10
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
11
|
+
import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint'
|
|
12
|
+
import { firstPaint } from '../../../common/vitals/first-paint'
|
|
13
|
+
import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte'
|
|
15
14
|
|
|
16
15
|
export class Aggregate extends AggregateBase {
|
|
17
16
|
static featureName = CONSTANTS.FEATURE_NAME
|
|
18
17
|
constructor (agentIdentifier, aggregator) {
|
|
19
18
|
super(agentIdentifier, aggregator, CONSTANTS.FEATURE_NAME)
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
this.timeToFirstByte = 0
|
|
21
|
+
this.firstByteToWindowLoad = 0 // our "frontend" duration
|
|
22
|
+
this.firstByteToDomContent = 0 // our "dom processing" duration
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
onTTFB(({ value, entries }) => {
|
|
28
|
-
if (this.alreadySent) return
|
|
29
|
-
this.alreadySent = true
|
|
30
|
-
|
|
31
|
-
agentRuntime[CONSTANTS.TTFB] = Math.round(value) // this is our "backend" duration; web-vitals will ensure it's lower bounded at 0
|
|
32
|
-
|
|
33
|
-
// Similar to what vitals does for ttfb, we have to factor in activation-start when calculating relative timings:
|
|
24
|
+
if (isBrowserScope) {
|
|
25
|
+
timeToFirstByte.subscribe(({ value, entries }) => {
|
|
34
26
|
const navEntry = entries[0]
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
agentRuntime[CONSTANTS.FBTDC] = Math.max(Math.round(navEntry.domContentLoadedEventEnd - respOrActivStart), 0) // our "dom processing" duration
|
|
27
|
+
this.timeToFirstByte = Math.max(value, this.timeToFirstByte)
|
|
28
|
+
this.firstByteToWindowLoad = Math.max(Math.round(navEntry.loadEventEnd - this.timeToFirstByte), this.firstByteToWindowLoad) // our "frontend" duration
|
|
29
|
+
this.firstByteToDomContent = Math.max(Math.round(navEntry.domContentLoadedEventEnd - this.timeToFirstByte), this.firstByteToDomContent) // our "dom processing" duration
|
|
39
30
|
|
|
40
31
|
this.sendRum()
|
|
41
32
|
})
|
|
42
33
|
} else {
|
|
43
|
-
|
|
34
|
+
// worker agent build does not get TTFB values, use default 0 values
|
|
35
|
+
this.sendRum()
|
|
44
36
|
}
|
|
45
37
|
}
|
|
46
38
|
|
|
47
|
-
getScheme () {
|
|
48
|
-
return getConfigurationValue(this.agentIdentifier, 'ssl') === false ? 'http' : 'https'
|
|
49
|
-
}
|
|
50
|
-
|
|
51
39
|
sendRum () {
|
|
52
40
|
const info = getInfo(this.agentIdentifier)
|
|
53
41
|
const agentRuntime = getRuntime(this.agentIdentifier)
|
|
@@ -60,9 +48,9 @@ export class Aggregate extends AggregateBase {
|
|
|
60
48
|
// These 3 values should've been recorded after load and before this func runs. They are part of the minimum required for PageView events to be created.
|
|
61
49
|
// Following PR #428, which demands that all agents send RUM call, these need to be sent even outside of the main window context where PerformanceTiming
|
|
62
50
|
// or PerformanceNavigationTiming do not exists. Hence, they'll be filled in by 0s instead in, for example, worker threads that still init the PVE module.
|
|
63
|
-
this.aggregator.store('measures', 'be', { value:
|
|
64
|
-
this.aggregator.store('measures', 'fe', { value:
|
|
65
|
-
this.aggregator.store('measures', 'dc', { value:
|
|
51
|
+
this.aggregator.store('measures', 'be', { value: this.timeToFirstByte })
|
|
52
|
+
this.aggregator.store('measures', 'fe', { value: this.firstByteToWindowLoad })
|
|
53
|
+
this.aggregator.store('measures', 'dc', { value: this.firstByteToDomContent })
|
|
66
54
|
|
|
67
55
|
const queryParameters = {
|
|
68
56
|
tt: info.ttGuid,
|
|
@@ -103,19 +91,8 @@ export class Aggregate extends AggregateBase {
|
|
|
103
91
|
}
|
|
104
92
|
}
|
|
105
93
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
entries.forEach(function (entry) {
|
|
109
|
-
if (!entry.startTime || entry.startTime <= 0) return
|
|
110
|
-
|
|
111
|
-
if (entry.name === 'first-paint') {
|
|
112
|
-
queryParameters.fp = String(Math.floor(entry.startTime))
|
|
113
|
-
} else if (entry.name === 'first-contentful-paint') {
|
|
114
|
-
queryParameters.fcp = String(Math.floor(entry.startTime))
|
|
115
|
-
}
|
|
116
|
-
paintMetrics[entry.name] = Math.floor(entry.startTime) // this is consumed by Spa module
|
|
117
|
-
})
|
|
118
|
-
} catch (e) {}
|
|
94
|
+
queryParameters.fp = firstPaint.current.value
|
|
95
|
+
queryParameters.fcp = firstContentfulPaint.current.value
|
|
119
96
|
|
|
120
97
|
harvester.send({
|
|
121
98
|
endpoint: 'rum',
|
|
@@ -1,32 +1,11 @@
|
|
|
1
|
-
import { handle } from '../../../common/event-emitter/handle'
|
|
2
|
-
import { isiOS } from '../../../common/constants/runtime'
|
|
3
1
|
import { InstrumentBase } from '../../utils/instrument-base'
|
|
4
2
|
import * as CONSTANTS from '../constants'
|
|
5
|
-
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
6
|
-
import { getRuntime } from '../../../common/config/config'
|
|
7
|
-
import { onDOMContentLoaded, onWindowLoad } from '../../../common/window/load'
|
|
8
|
-
import { now } from '../../../common/timing/now'
|
|
9
3
|
|
|
10
4
|
export class Instrument extends InstrumentBase {
|
|
11
5
|
static featureName = CONSTANTS.FEATURE_NAME
|
|
12
6
|
constructor (agentIdentifier, aggregator, auto = true) {
|
|
13
7
|
super(agentIdentifier, aggregator, CONSTANTS.FEATURE_NAME, auto)
|
|
14
8
|
|
|
15
|
-
if ((typeof PerformanceNavigationTiming === 'undefined' || isiOS) && typeof PerformanceTiming !== 'undefined') {
|
|
16
|
-
// For majority browser versions in which PNT exists, we can get load timings later from the nav entry (in the aggregate portion). At minimum, PT should exist for main window.
|
|
17
|
-
// *cli Mar'23 - iOS 15.2 & 15.4 testing in Sauce still fails with onTTFB. Hence, all iOS will fallback to this for now. Unknown if this is similar in nature to iOSBelow16 bug.
|
|
18
|
-
const agentRuntime = getRuntime(agentIdentifier)
|
|
19
|
-
|
|
20
|
-
agentRuntime[CONSTANTS.TTFB] = Math.max(Date.now() - agentRuntime.offset, 0)
|
|
21
|
-
onDOMContentLoaded(() => { agentRuntime[CONSTANTS.FBTDC] = Math.max(now() - agentRuntime[CONSTANTS.TTFB], 0) })
|
|
22
|
-
onWindowLoad(() => {
|
|
23
|
-
const timeNow = now()
|
|
24
|
-
agentRuntime[CONSTANTS.FBTWL] = Math.max(timeNow - agentRuntime[CONSTANTS.TTFB], 0)
|
|
25
|
-
handle('timing', ['load', timeNow], undefined, FEATURE_NAMES.pageViewTiming, this.ee)
|
|
26
|
-
})
|
|
27
|
-
}
|
|
28
|
-
// Else, inference: inside worker or some other env where these events are irrelevant. They'll get filled in with 0s in RUM call.
|
|
29
|
-
|
|
30
9
|
this.importAggregator()
|
|
31
10
|
}
|
|
32
11
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Aggregator } from '../../../common/aggregate/aggregator'
|
|
2
|
+
import { ee } from '../../../common/event-emitter/contextual-ee'
|
|
3
|
+
import { drain } from '../../../common/drain/drain'
|
|
4
|
+
import { setRuntime } from '../../../common/config/config'
|
|
5
|
+
|
|
6
|
+
jest.mock('web-vitals', () => ({
|
|
7
|
+
__esModule: true,
|
|
8
|
+
// eslint-disable-next-line
|
|
9
|
+
onFID: jest.fn(cb => cb({
|
|
10
|
+
value: 1234,
|
|
11
|
+
entries: [{ name: 'pointerdown', startTime: 5 }]
|
|
12
|
+
})),
|
|
13
|
+
// eslint-disable-next-line
|
|
14
|
+
onCLS: jest.fn((cb) => cb({
|
|
15
|
+
value: 1,
|
|
16
|
+
entries: [{ value: 1 }]
|
|
17
|
+
})),
|
|
18
|
+
// eslint-disable-next-line
|
|
19
|
+
onFCP: jest.fn((cb) => cb({
|
|
20
|
+
value: 1,
|
|
21
|
+
entries: [{ value: 1 }]
|
|
22
|
+
})),
|
|
23
|
+
// eslint-disable-next-line
|
|
24
|
+
onINP: jest.fn((cb) => cb({
|
|
25
|
+
value: 1,
|
|
26
|
+
entries: [{ value: 1 }]
|
|
27
|
+
})),
|
|
28
|
+
// eslint-disable-next-line
|
|
29
|
+
onLCP: jest.fn((cb) => cb({
|
|
30
|
+
value: 1,
|
|
31
|
+
entries: [{ value: 1 }]
|
|
32
|
+
}))
|
|
33
|
+
})
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
let pvtAgg, cumulativeLayoutShift
|
|
37
|
+
describe('pvt aggregate tests', () => {
|
|
38
|
+
beforeEach(async () => {
|
|
39
|
+
const { Aggregate } = await import('.')
|
|
40
|
+
setRuntime('abcd', {})
|
|
41
|
+
pvtAgg = new Aggregate('abcd', new Aggregator({ agentIdentifier: 'abcd', ee }))
|
|
42
|
+
pvtAgg.scheduler.harvest.send = jest.fn()
|
|
43
|
+
pvtAgg.prepareHarvest = jest.fn(() => ({}))
|
|
44
|
+
drain('abcd', 'feature')
|
|
45
|
+
|
|
46
|
+
global.navigator.connection = {
|
|
47
|
+
type: 'cellular',
|
|
48
|
+
effectiveType: '3g',
|
|
49
|
+
rtt: 270,
|
|
50
|
+
downlink: 700
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const { cumulativeLayoutShift: cls } = await import('../../../common/vitals/cumulative-layout-shift')
|
|
54
|
+
cumulativeLayoutShift = cls
|
|
55
|
+
})
|
|
56
|
+
test('LCP event with CLS attribute', () => {
|
|
57
|
+
cumulativeLayoutShift.update({ value: 1 })
|
|
58
|
+
pvtAgg.addTiming('lcp', 1, { size: 1, startTime: 1 })
|
|
59
|
+
|
|
60
|
+
var timing = find(pvtAgg.timings, function (t) {
|
|
61
|
+
return t.name === 'lcp'
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
expect(timing.attrs.cls).toEqual(1) // 'CLS value should be the one present at the time LCP happened'
|
|
65
|
+
|
|
66
|
+
function find (arr, fn) {
|
|
67
|
+
if (arr.find) {
|
|
68
|
+
return arr.find(fn)
|
|
69
|
+
}
|
|
70
|
+
var match = null
|
|
71
|
+
arr.forEach(function (t) {
|
|
72
|
+
if (fn(t)) {
|
|
73
|
+
match = t
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
return match
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('sends expected FI attributes when available', () => {
|
|
81
|
+
expect(pvtAgg.timings.length).toBeGreaterThanOrEqual(1)
|
|
82
|
+
const fiPayload = pvtAgg.timings.find(x => x.name === 'fi')
|
|
83
|
+
expect(fiPayload.value).toEqual(5)
|
|
84
|
+
expect(fiPayload.attrs).toEqual(expect.objectContaining({ type: 'pointerdown', fid: 1234, cls: 1 }))
|
|
85
|
+
})
|
|
86
|
+
})
|
|
@@ -3,146 +3,69 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { onFCP, onFID, onLCP, onCLS, onINP } from 'web-vitals'
|
|
7
|
-
import { onFirstPaint } from '../first-paint'
|
|
8
|
-
import { onLongTask } from '../long-tasks'
|
|
9
|
-
import { iOSBelow16 } from '../../../common/constants/runtime'
|
|
10
6
|
import { nullable, numeric, getAddStringContext, addCustomAttributes } from '../../../common/serialize/bel-serializer'
|
|
11
7
|
import { mapOwn } from '../../../common/util/map-own'
|
|
12
8
|
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
|
|
13
9
|
import { registerHandler } from '../../../common/event-emitter/register-handler'
|
|
14
|
-
import { cleanURL } from '../../../common/url/clean-url'
|
|
15
10
|
import { handle } from '../../../common/event-emitter/handle'
|
|
16
|
-
import { getInfo, getConfigurationValue
|
|
11
|
+
import { getInfo, getConfigurationValue } from '../../../common/config/config'
|
|
17
12
|
import { FEATURE_NAME } from '../constants'
|
|
18
13
|
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
19
14
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
15
|
+
import { cumulativeLayoutShift } from '../../../common/vitals/cumulative-layout-shift'
|
|
16
|
+
import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint'
|
|
17
|
+
import { firstInputDelay } from '../../../common/vitals/first-input-delay'
|
|
18
|
+
import { firstPaint } from '../../../common/vitals/first-paint'
|
|
19
|
+
import { interactionToNextPaint } from '../../../common/vitals/interaction-to-next-paint'
|
|
20
|
+
import { largestContentfulPaint } from '../../../common/vitals/largest-contentful-paint'
|
|
21
|
+
import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte'
|
|
22
|
+
import { longTask } from '../../../common/vitals/long-task'
|
|
20
23
|
|
|
21
24
|
export class Aggregate extends AggregateBase {
|
|
22
25
|
static featureName = FEATURE_NAME
|
|
26
|
+
|
|
27
|
+
#handleVitalMetric = ({ name, value, attrs }) => {
|
|
28
|
+
this.addTiming(name, value, attrs)
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
constructor (agentIdentifier, aggregator) {
|
|
24
32
|
super(agentIdentifier, aggregator, FEATURE_NAME)
|
|
25
33
|
|
|
26
34
|
this.timings = []
|
|
27
35
|
this.timingsSent = []
|
|
28
36
|
this.curSessEndRecorded = false
|
|
29
|
-
this.cls = null // this should be null unless set to a numeric value by web-vitals so that we differentiate if CLS is supported
|
|
30
|
-
|
|
31
|
-
/* ! This is the section that used to be in the loader portion: ! */
|
|
32
|
-
/* ------------------------------------------------------------ */
|
|
33
|
-
const pageStartedHidden = getRuntime(agentIdentifier).initHidden // our attempt at recapturing initial vis state since this code runs post-load time
|
|
34
|
-
this.alreadySent = new Set() // since we don't support timings on BFCache restores, this tracks and helps cap metrics that web-vitals report more than once
|
|
35
|
-
|
|
36
|
-
/* PerformancePaintTiming API - BFC is not yet supported. */
|
|
37
|
-
onFirstPaint(({ name, value }) => {
|
|
38
|
-
if (pageStartedHidden) return
|
|
39
|
-
this.addTiming(name.toLowerCase(), Math.floor(value))
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
/* 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. */
|
|
43
|
-
if (iOSBelow16) {
|
|
44
|
-
try {
|
|
45
|
-
if (!pageStartedHidden) { // see ios-version.js for detail on this following bug case; tldr: buffered flag doesn't work but getEntriesByType does
|
|
46
|
-
const paintEntries = performance.getEntriesByType('paint')
|
|
47
|
-
paintEntries.forEach(entry => {
|
|
48
|
-
if (entry.name === 'first-contentful-paint') {
|
|
49
|
-
this.addTiming('fcp', Math.floor(entry.startTime))
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
} catch (e) {}
|
|
54
|
-
} else {
|
|
55
|
-
onFCP(({ name, value }) => {
|
|
56
|
-
if (pageStartedHidden || this.alreadySent.has(name)) return
|
|
57
|
-
this.alreadySent.add(name)
|
|
58
|
-
this.addTiming(name.toLowerCase(), value)
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/* First Input Delay (+"First Interaction") - As of WV v3, it still imperfectly tries to detect document vis state asap and isn't supposed to report if page starts hidden. */
|
|
63
|
-
onFID(({ name, value, entries }) => {
|
|
64
|
-
if (pageStartedHidden || this.alreadySent.has(name) || entries.length === 0) return
|
|
65
|
-
this.alreadySent.add(name)
|
|
66
|
-
|
|
67
|
-
// 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.
|
|
68
|
-
const fiEntry = entries[0]
|
|
69
|
-
const attributes = {
|
|
70
|
-
type: fiEntry.name,
|
|
71
|
-
fid: Math.round(value)
|
|
72
|
-
}
|
|
73
|
-
this.addConnectionAttributes(attributes)
|
|
74
|
-
this.addTiming('fi', Math.round(fiEntry.startTime), attributes)
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
/* Largest 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. */
|
|
78
|
-
onLCP(({ name, value, entries }) => {
|
|
79
|
-
if (pageStartedHidden || this.alreadySent.has(name)) return
|
|
80
|
-
this.alreadySent.add(name)
|
|
81
|
-
|
|
82
|
-
const attributes = {}
|
|
83
|
-
if (entries.length > 0) {
|
|
84
|
-
// CWV will only ever report one (THE) lcp entry to us; lcp is also only reported *once* on earlier(user interaction, page hidden).
|
|
85
|
-
const lcpEntry = entries[entries.length - 1] // this looks weird if we only expect one, but this is how cwv-attribution gets it so to be sure...
|
|
86
|
-
attributes.size = lcpEntry.size
|
|
87
|
-
attributes.eid = lcpEntry.id
|
|
88
|
-
|
|
89
|
-
if (lcpEntry.url) {
|
|
90
|
-
attributes.elUrl = cleanURL(lcpEntry.url)
|
|
91
|
-
}
|
|
92
|
-
if (lcpEntry.element?.tagName) {
|
|
93
|
-
attributes.elTag = lcpEntry.element.tagName
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
37
|
|
|
97
|
-
|
|
98
|
-
|
|
38
|
+
firstPaint.subscribe(this.#handleVitalMetric)
|
|
39
|
+
firstContentfulPaint.subscribe(this.#handleVitalMetric)
|
|
40
|
+
firstInputDelay.subscribe(this.#handleVitalMetric)
|
|
41
|
+
largestContentfulPaint.subscribe(this.#handleVitalMetric)
|
|
42
|
+
interactionToNextPaint.subscribe(this.#handleVitalMetric)
|
|
43
|
+
timeToFirstByte.subscribe(({ entries }) => {
|
|
44
|
+
this.addTiming('load', Math.round(entries[0].loadEventEnd))
|
|
99
45
|
})
|
|
100
46
|
|
|
101
|
-
|
|
102
|
-
reportAllChanges ensures our tracked cls has the most recent rolling value to attach to 'unload' and 'pagehide'. */
|
|
103
|
-
onCLS(({ value }) => { this.cls = value }, { reportAllChanges: true })
|
|
104
|
-
|
|
105
|
-
/* Interaction-to-Next-Paint */
|
|
106
|
-
onINP(({ name, value, id }) => this.addTiming(name.toLowerCase(), value, { metricId: id }))
|
|
107
|
-
|
|
108
|
-
/* PerformanceLongTaskTiming API */
|
|
109
|
-
if (getConfigurationValue(this.agentIdentifier, 'page_view_timing.long_task') === true) {
|
|
110
|
-
onLongTask(({ name, value, info }) => this.addTiming(name.toLowerCase(), value, info)) // lt context is passed as 'info'=attrs in the timing node
|
|
111
|
-
}
|
|
112
|
-
/* ------------------------------------End of ex-loader section */
|
|
47
|
+
if (getConfigurationValue(this.agentIdentifier, 'page_view_timing.long_task') === true) longTask.subscribe(this.#handleVitalMetric)
|
|
113
48
|
|
|
114
49
|
/* It's important that CWV api, like "onLCP", is called before this scheduler is initialized. The reason is because they listen to the same
|
|
115
50
|
on vis change or pagehide events, and we'd want ex. onLCP to record the timing (win the race) before we try to send "final harvest". */
|
|
116
|
-
this.scheduler = new HarvestScheduler('events', {
|
|
117
|
-
onFinished: (...args) => this.onHarvestFinished(...args),
|
|
118
|
-
getPayload: (...args) => this.prepareHarvest(...args)
|
|
119
|
-
}, this)
|
|
120
51
|
|
|
121
|
-
registerHandler('timing', (name, value, attrs) => this.addTiming(name, value, attrs), this.featureName, this.ee) // notice CLS is added to all timings via 4th param
|
|
122
52
|
registerHandler('docHidden', msTimestamp => this.endCurrentSession(msTimestamp), this.featureName, this.ee)
|
|
123
53
|
registerHandler('winPagehide', msTimestamp => this.recordPageUnload(msTimestamp), this.featureName, this.ee)
|
|
124
54
|
|
|
125
55
|
const initialHarvestSeconds = getConfigurationValue(this.agentIdentifier, 'page_view_timing.initialHarvestSeconds') || 10
|
|
126
56
|
const harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'page_view_timing.harvestTimeSeconds') || 30
|
|
127
57
|
// send initial data sooner, then start regular
|
|
128
|
-
this.ee.on(`drain-${this.featureName}`, () => {
|
|
58
|
+
this.ee.on(`drain-${this.featureName}`, () => {
|
|
59
|
+
this.scheduler = new HarvestScheduler('events', {
|
|
60
|
+
onFinished: (...args) => this.onHarvestFinished(...args),
|
|
61
|
+
getPayload: (...args) => this.prepareHarvest(...args)
|
|
62
|
+
}, this)
|
|
63
|
+
this.scheduler.startTimer(harvestTimeSeconds, initialHarvestSeconds)
|
|
64
|
+
})
|
|
129
65
|
|
|
130
66
|
this.drain()
|
|
131
67
|
}
|
|
132
68
|
|
|
133
|
-
// takes an attributes object and appends connection attributes if available
|
|
134
|
-
addConnectionAttributes (attributes) {
|
|
135
|
-
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection // to date, both window & worker shares the same support for connection
|
|
136
|
-
if (!connection) return
|
|
137
|
-
|
|
138
|
-
if (connection.type) attributes['net-type'] = connection.type
|
|
139
|
-
if (connection.effectiveType) attributes['net-etype'] = connection.effectiveType
|
|
140
|
-
if (connection.rtt) attributes['net-rtt'] = connection.rtt
|
|
141
|
-
if (connection.downlink) attributes['net-dlink'] = connection.downlink
|
|
142
|
-
|
|
143
|
-
return attributes
|
|
144
|
-
}
|
|
145
|
-
|
|
146
69
|
/**
|
|
147
70
|
* Add the time of _document visibilitychange to hidden_ to the next PVT harvest == NRDB pageHide attr.
|
|
148
71
|
* @param {number} timestamp
|
|
@@ -179,8 +102,8 @@ export class Aggregate extends AggregateBase {
|
|
|
179
102
|
Mitigation: We've set initial CLS to null so that it's omitted from timings like 'pageHide' in that edge case. It should only be included if onCLS callback was executed at least once.
|
|
180
103
|
Future: onCLS value changes should be reported directly & CLS separated into its own timing node so it's not beholden to 'pageHide' firing. It'd also be possible to report the real final CLS.
|
|
181
104
|
*/
|
|
182
|
-
if (
|
|
183
|
-
attrs.cls =
|
|
105
|
+
if (cumulativeLayoutShift.current.value >= 0) {
|
|
106
|
+
attrs.cls = cumulativeLayoutShift.current.value
|
|
184
107
|
}
|
|
185
108
|
|
|
186
109
|
this.timings.push({
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { handle } from '../../../common/event-emitter/handle'
|
|
6
|
-
import { getRuntime } from '../../../common/config/config'
|
|
7
6
|
import { subscribeToVisibilityChange } from '../../../common/window/page-visibility'
|
|
8
7
|
import { windowAddEventListener } from '../../../common/event-listener/event-listener-opts'
|
|
9
8
|
import { now } from '../../../common/timing/now'
|
|
@@ -17,9 +16,7 @@ export class Instrument extends InstrumentBase {
|
|
|
17
16
|
super(agentIdentifier, aggregator, FEATURE_NAME, auto)
|
|
18
17
|
if (!isBrowserScope) return // CWV is irrelevant outside web context
|
|
19
18
|
|
|
20
|
-
// Document visibility state becomes hidden; this should run as soon as possible in page life.
|
|
21
19
|
// While we try to replicate web-vital's visibilitywatcher logic in an effort to defer that library to post-pageload, this isn't perfect and doesn't consider prerendering.
|
|
22
|
-
getRuntime(agentIdentifier).initHidden = Boolean(document.visibilityState === 'hidden')
|
|
23
20
|
subscribeToVisibilityChange(() => handle('docHidden', [now()], undefined, FEATURE_NAME, this.ee), true)
|
|
24
21
|
|
|
25
22
|
// Window fires its pagehide event (typically on navigation--this occurrence is a *subset* of vis change); don't defer this unless it's guarantee it cannot happen before load(?)
|
|
@@ -39,7 +39,7 @@ class LocalMemory {
|
|
|
39
39
|
let sr, session
|
|
40
40
|
const agentIdentifier = 'abcd'
|
|
41
41
|
const info = { licenseKey: 1234, applicationID: 9876 }
|
|
42
|
-
const init = { session_replay: { enabled: true,
|
|
42
|
+
const init = { session_replay: { enabled: true, sampling_rate: 100, error_sampling_rate: 0 } }
|
|
43
43
|
|
|
44
44
|
const anyQuery = {
|
|
45
45
|
browser_monitoring_key: info.licenseKey,
|
|
@@ -99,14 +99,14 @@ describe('Session Replay', () => {
|
|
|
99
99
|
})
|
|
100
100
|
|
|
101
101
|
test('Session SR mode matches SR mode -- ERROR', async () => {
|
|
102
|
-
setConfiguration(agentIdentifier, { session_replay: {
|
|
102
|
+
setConfiguration(agentIdentifier, { session_replay: { sampling_rate: 0, error_sampling_rate: 100 } })
|
|
103
103
|
sr.ee.emit('rumresp-sr', [true])
|
|
104
104
|
await wait(1)
|
|
105
105
|
expect(session.state.sessionReplay).toEqual(sr.mode)
|
|
106
106
|
})
|
|
107
107
|
|
|
108
108
|
test('Session SR mode matches SR mode -- OFF', async () => {
|
|
109
|
-
setConfiguration(agentIdentifier, { session_replay: {
|
|
109
|
+
setConfiguration(agentIdentifier, { session_replay: { sampling_rate: 0, error_sampling_rate: 0 } })
|
|
110
110
|
sr.ee.emit('rumresp-sr', [true])
|
|
111
111
|
await wait(1)
|
|
112
112
|
expect(session.state.sessionReplay).toEqual(sr.mode)
|
|
@@ -149,28 +149,28 @@ describe('Session Replay', () => {
|
|
|
149
149
|
|
|
150
150
|
describe('Session Replay Sample -> Mode Behaviors', () => {
|
|
151
151
|
test('New Session -- Full 1 Error 1 === FULL', async () => {
|
|
152
|
-
setConfiguration(agentIdentifier, { session_replay: {
|
|
152
|
+
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 100 } })
|
|
153
153
|
sr.ee.emit('rumresp-sr', [true])
|
|
154
154
|
await wait(1)
|
|
155
155
|
expect(sr.mode).toEqual(MODE.FULL)
|
|
156
156
|
})
|
|
157
157
|
|
|
158
158
|
test('New Session -- Full 1 Error 0 === FULL', async () => {
|
|
159
|
-
setConfiguration(agentIdentifier, { session_replay: {
|
|
159
|
+
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 0, sampling_rate: 100 } })
|
|
160
160
|
sr.ee.emit('rumresp-sr', [true])
|
|
161
161
|
await wait(1)
|
|
162
162
|
expect(sr.mode).toEqual(MODE.FULL)
|
|
163
163
|
})
|
|
164
164
|
|
|
165
165
|
test('New Session -- Full 0 Error 1 === ERROR', async () => {
|
|
166
|
-
setConfiguration(agentIdentifier, { session_replay: {
|
|
166
|
+
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
|
|
167
167
|
sr.ee.emit('rumresp-sr', [true])
|
|
168
168
|
await wait(1)
|
|
169
169
|
expect(sr.mode).toEqual(MODE.ERROR)
|
|
170
170
|
})
|
|
171
171
|
|
|
172
172
|
test('New Session -- Full 0 Error 0 === OFF', async () => {
|
|
173
|
-
setConfiguration(agentIdentifier, { session_replay: {
|
|
173
|
+
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 0, sampling_rate: 0 } })
|
|
174
174
|
sr.ee.emit('rumresp-sr', [true])
|
|
175
175
|
await wait(1)
|
|
176
176
|
expect(sr.mode).toEqual(MODE.OFF)
|
|
@@ -182,7 +182,7 @@ describe('Session Replay', () => {
|
|
|
182
182
|
expect(session.isNew).toBeFalsy()
|
|
183
183
|
primeSessionAndReplay(session)
|
|
184
184
|
// configure to get "error" sample ---> but should inherit FULL from session manager
|
|
185
|
-
setConfiguration(agentIdentifier, { session_replay: {
|
|
185
|
+
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
|
|
186
186
|
sr.ee.emit('rumresp-sr', [true])
|
|
187
187
|
await wait(1)
|
|
188
188
|
expect(sr.mode).toEqual(MODE.FULL)
|
|
@@ -191,7 +191,7 @@ describe('Session Replay', () => {
|
|
|
191
191
|
|
|
192
192
|
describe('Session Replay Error Mode Behaviors', () => {
|
|
193
193
|
test('An error BEFORE rrweb import starts running in FULL from beginning', async () => {
|
|
194
|
-
setConfiguration(agentIdentifier, { session_replay: {
|
|
194
|
+
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
|
|
195
195
|
sr.ee.emit('errorAgg')
|
|
196
196
|
sr.ee.emit('rumresp-sr', [true])
|
|
197
197
|
await wait(1)
|
|
@@ -200,7 +200,7 @@ describe('Session Replay', () => {
|
|
|
200
200
|
})
|
|
201
201
|
|
|
202
202
|
test('An error AFTER rrweb import changes mode and starts harvester', async () => {
|
|
203
|
-
setConfiguration(agentIdentifier, { session_replay: {
|
|
203
|
+
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
|
|
204
204
|
sr.ee.emit('rumresp-sr', [true])
|
|
205
205
|
await wait(1)
|
|
206
206
|
expect(sr.mode).toEqual(MODE.ERROR)
|