Skip to content

Commit e0dd7ee

Browse files
committed
♻️ do not report subparts when TTFB returns undefined
1 parent 94434b0 commit e0dd7ee

File tree

2 files changed

+49
-10
lines changed

2 files changed

+49
-10
lines changed

packages/rum-core/src/domain/view/viewMetrics/trackLargestContentfulPaint.spec.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,37 @@ describe('trackLargestContentfulPaint', () => {
189189
subParts: mockSubParts,
190190
})
191191
})
192+
193+
it('should not provide subParts if the first byte is not available', () => {
194+
const { notifyPerformanceEntries: notifyEntries } = mockPerformanceObserver()
195+
196+
// Notify navigation entry with negative responseStart, which makes getSafeFirstByte return undefined
197+
notifyEntries([
198+
createPerformanceEntry(RumPerformanceEntryType.NAVIGATION, {
199+
responseStart: -1 as RelativeTime,
200+
}),
201+
])
202+
203+
const firstHidden = trackFirstHidden(mockRumConfiguration(), clocksOrigin())
204+
const largestContentfulPaint = trackLargestContentfulPaint(
205+
mockRumConfiguration(),
206+
firstHidden,
207+
eventTarget,
208+
lcpCallback
209+
)
210+
211+
registerCleanupTask(() => {
212+
firstHidden.stop()
213+
largestContentfulPaint.stop()
214+
})
215+
216+
notifyEntries([createPerformanceEntry(RumPerformanceEntryType.LARGEST_CONTENTFUL_PAINT)])
217+
218+
expect(lcpCallback).toHaveBeenCalledOnceWith({
219+
value: 789 as RelativeTime,
220+
targetSelector: undefined,
221+
resourceUrl: undefined,
222+
subParts: undefined,
223+
})
224+
})
192225
})

packages/rum-core/src/domain/view/viewMetrics/trackLargestContentfulPaint.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,21 +77,27 @@ export function trackLargestContentfulPaint(
7777
? (getResourceEntries()?.find((e) => e.name === resourceUrl) as PerformanceResourceTiming | undefined)
7878
: undefined
7979

80-
const firstByte = getSafeFirstByte(getNavigationEntry()) || (0 as RelativeTime)
81-
const lcpRequestStart = Math.max(firstByte, lcpResourceEntry?.startTime || 0)
82-
const lcpResponseEnd = Math.max(lcpRequestStart, lcpResourceEntry?.responseEnd || 0)
83-
const lcpRenderTime = Math.max(lcpResponseEnd, lcpEntry.startTime)
80+
let subParts;
81+
const firstByte = getSafeFirstByte(getNavigationEntry())
8482

85-
callback({
86-
value: lcpEntry.startTime,
87-
targetSelector: lcpTargetSelector,
88-
resourceUrl,
89-
subParts: {
83+
if (firstByte !== undefined) {
84+
const lcpRequestStart = Math.max(firstByte, lcpResourceEntry?.startTime || 0)
85+
const lcpResponseEnd = Math.max(lcpRequestStart, lcpResourceEntry?.responseEnd || 0)
86+
const lcpRenderTime = Math.max(lcpResponseEnd, lcpEntry.startTime);
87+
88+
subParts = {
9089
firstByte,
9190
loadDelay: (lcpRequestStart - firstByte) as RelativeTime,
9291
loadTime: (lcpResponseEnd - lcpRequestStart) as RelativeTime,
9392
renderDelay: (lcpRenderTime - lcpResponseEnd) as RelativeTime,
94-
},
93+
}
94+
}
95+
96+
callback({
97+
value: lcpEntry.startTime,
98+
targetSelector: lcpTargetSelector,
99+
resourceUrl,
100+
subParts,
95101
})
96102
biggestLcpSize = lcpEntry.size
97103
}

0 commit comments

Comments
 (0)