From 79930f1720bdbbc260fc083a53540293e8bb924c Mon Sep 17 00:00:00 2001 From: Krystof Woldrich <31292499+krystofwoldrich@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:29:08 +0200 Subject: [PATCH] feat(ttd): `TimeToInitialDisplay` and `TimeToFullDisplay` start the time to display spans on mount (#4020) --- CHANGELOG.md | 4 ++ src/js/tracing/timetodisplay.tsx | 2 + test/tracing/timetodisplay.test.tsx | 57 ++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b0ba8612..23cd8cf07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- `TimeToInitialDisplay` and `TimeToFullDisplay` start the time to display spans on mount ([#4020](https://github.com/getsentry/sentry-react-native/pull/4020)) + ### Fixed - fix(ttid): End and measure TTID regardless current active span ([#4019](https://github.com/getsentry/sentry-react-native/pull/4019)) diff --git a/src/js/tracing/timetodisplay.tsx b/src/js/tracing/timetodisplay.tsx index 972374d94..378ecfca5 100644 --- a/src/js/tracing/timetodisplay.tsx +++ b/src/js/tracing/timetodisplay.tsx @@ -36,6 +36,7 @@ export function TimeToInitialDisplay(props: TimeToDisplayProps): React.ReactElem const activeSpan = getActiveSpan(); if (activeSpan) { manualInitialDisplaySpans.set(activeSpan, true); + startTimeToInitialDisplaySpan(); } return {props.children}; @@ -49,6 +50,7 @@ export function TimeToInitialDisplay(props: TimeToDisplayProps): React.ReactElem * */ export function TimeToFullDisplay(props: TimeToDisplayProps): React.ReactElement { + startTimeToFullDisplaySpan(); return {props.children}; } diff --git a/test/tracing/timetodisplay.test.tsx b/test/tracing/timetodisplay.test.tsx index 9abe22bed..a70105ae5 100644 --- a/test/tracing/timetodisplay.test.tsx +++ b/test/tracing/timetodisplay.test.tsx @@ -2,7 +2,7 @@ import * as mockedtimetodisplaynative from './mockedtimetodisplaynative'; jest.mock('../../src/js/tracing/timetodisplaynative', () => mockedtimetodisplaynative); import type { Span as SpanClass} from '@sentry/core'; -import { getCurrentScope, getGlobalScope, getIsolationScope, setCurrentClient, spanToJSON, startSpanManual} from '@sentry/core'; +import { getActiveSpan, getCurrentScope, getGlobalScope, getIsolationScope, setCurrentClient, spanToJSON, startSpanManual} from '@sentry/core'; import type { Event, Measurements, Span, SpanJSON} from '@sentry/types'; import React from "react"; import TestRenderer from 'react-test-renderer'; @@ -92,6 +92,61 @@ describe('TimeToDisplay', () => { expect(spanToJSON(testSpan!).start_timestamp).toEqual(spanToJSON(activeSpan!).start_timestamp); }); + test('creates initial display span on first component render', async () => { + const [testSpan, activeSpan] = startSpanManual( + { + name: 'Root Manual Span', + startTime: secondAgoTimestampMs(), + }, + (activeSpan: Span | undefined) => { + const renderer = TestRenderer.create(); + const testSpan = (getActiveSpan() as SpanClass).spanRecorder?.spans.find((span) => spanToJSON(span).op === 'ui.load.initial_display'); + + renderer.update(); + emitNativeInitialDisplayEvent(); + + activeSpan?.end(); + return [testSpan, activeSpan]; + }, + ); + + await jest.runOnlyPendingTimersAsync(); + await client.flush(); + + expectInitialDisplayMeasurementOnSpan(client.event!); + expectFinishedInitialDisplaySpan(testSpan, activeSpan); + expect(spanToJSON(testSpan!).start_timestamp).toEqual(spanToJSON(activeSpan!).start_timestamp); + }); + + test('creates full display span on first component render', async () => { + const [testSpan, activeSpan] = startSpanManual( + { + name: 'Root Manual Span', + startTime: secondAgoTimestampMs(), + }, + (activeSpan: Span | undefined) => { + TestRenderer.create(); + emitNativeInitialDisplayEvent(); + + const renderer = TestRenderer.create(); + const testSpan = (getActiveSpan() as SpanClass).spanRecorder?.spans.find((span) => spanToJSON(span).op === 'ui.load.full_display'); + + renderer.update(); + emitNativeFullDisplayEvent(); + + activeSpan?.end(); + return [testSpan, activeSpan]; + }, + ); + + await jest.runOnlyPendingTimersAsync(); + await client.flush(); + + expectFullDisplayMeasurementOnSpan(client.event!); + expectFinishedFullDisplaySpan(testSpan, activeSpan); + expect(spanToJSON(testSpan!).start_timestamp).toEqual(spanToJSON(activeSpan!).start_timestamp); + }); + test('does not create full display when initial display is missing', async () => { const [activeSpan] = startSpanManual( {