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(
{