Skip to content

Commit a54077b

Browse files
committed
Fixes to tests
1 parent e6b6c07 commit a54077b

3 files changed

Lines changed: 16 additions & 288 deletions

File tree

packages/core/src/js/tracing/timetodisplay.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,12 @@ function createTimeToDisplay({
521521
};
522522
});
523523

524-
return <Component {...props} record={focused && props.record} />;
524+
// gate both legacy `record` and the new `ready` checkpoint on focus
525+
//
526+
// the idea here is that wrappers built via createTimeToFullDisplay/createTimeToInitialDisplay
527+
// can only record TTID/TTFD on a focused screen
528+
const gatedReady = props.ready === undefined ? undefined : focused && props.ready;
529+
return <Component {...props} record={focused && props.record} ready={gatedReady} />;
525530
};
526531

527532
TimeToDisplayWrapper.displayName = 'TimeToDisplayWrapper';

packages/core/test/tracing/timeToDisplayCoordinator.test.ts

Lines changed: 4 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
const SPAN_FIRST = 'span-first';
1212
const SPAN_SECOND = 'span-second';
1313

14-
/** Flush the coordinator's deferred up-flip timer. */
1514
function flushDefer(): void {
1615
jest.runOnlyPendingTimers();
1716
}
@@ -53,7 +52,7 @@ describe('timeToDisplayCoordinator', () => {
5352
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(true);
5453
});
5554

56-
test('late-registering not-ready checkpoint un-readies the aggregate', () => {
55+
test('late-registering not-ready checkpoint makes the aggregate "non-ready"', () => {
5756
registerCheckpoint('ttfd', SPAN_FIRST, 'a', true);
5857
registerCheckpoint('ttfd', SPAN_FIRST, 'b', true);
5958
flushDefer();
@@ -63,58 +62,6 @@ describe('timeToDisplayCoordinator', () => {
6362
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(false);
6463
});
6564

66-
test('unregistering the sole blocker keeps the aggregate not-ready (sticky)', () => {
67-
// A not-ready checkpoint that unmounts while it is the sole blocker is
68-
// kept in the registry to prevent a premature aggregate flip. Otherwise
69-
// a conditionally-rendered loading section that disappears before its
70-
// data resolves would silently record an incomplete display.
71-
registerCheckpoint('ttfd', SPAN_FIRST, 'a', true);
72-
const unregisterB = registerCheckpoint('ttfd', SPAN_FIRST, 'b', false);
73-
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(false);
74-
75-
unregisterB();
76-
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(false);
77-
// The sticky 'b' is still tracked so a subsequent component on the same
78-
// span continues to see it as a blocker.
79-
expect(hasAnyCheckpoints('ttfd', SPAN_FIRST)).toBe(true);
80-
});
81-
82-
test('unregistering a non-sole-blocker not-ready checkpoint removes it normally', () => {
83-
// When other not-ready checkpoints are still present, removing one
84-
// would not flip the aggregate, so the sticky safeguard does not apply.
85-
registerCheckpoint('ttfd', SPAN_FIRST, 'a', false);
86-
const unregisterB = registerCheckpoint('ttfd', SPAN_FIRST, 'b', false);
87-
88-
unregisterB();
89-
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(false);
90-
// 'a' remains, 'b' is gone.
91-
updateCheckpoint('ttfd', SPAN_FIRST, 'a', true);
92-
flushDefer();
93-
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(true);
94-
});
95-
96-
test('unregistering a ready checkpoint never goes sticky', () => {
97-
registerCheckpoint('ttfd', SPAN_FIRST, 'a', false);
98-
const unregisterB = registerCheckpoint('ttfd', SPAN_FIRST, 'b', true);
99-
100-
unregisterB();
101-
// 'a' is still blocking; 'b' was ready so removing it is safe.
102-
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(false);
103-
updateCheckpoint('ttfd', SPAN_FIRST, 'a', true);
104-
flushDefer();
105-
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(true);
106-
});
107-
108-
test('unregistering the last checkpoint leaves aggregate not-ready', () => {
109-
const unregister = registerCheckpoint('ttfd', SPAN_FIRST, 'a', true);
110-
flushDefer();
111-
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(true);
112-
113-
unregister();
114-
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(false);
115-
expect(hasAnyCheckpoints('ttfd', SPAN_FIRST)).toBe(false);
116-
});
117-
11865
test('different spans are independent', () => {
11966
registerCheckpoint('ttfd', SPAN_FIRST, 'a', true);
12067
registerCheckpoint('ttfd', SPAN_SECOND, 'a', false);
@@ -123,29 +70,14 @@ describe('timeToDisplayCoordinator', () => {
12370
expect(isAllReady('ttfd', SPAN_SECOND)).toBe(false);
12471
});
12572

126-
test('different kinds are independent', () => {
73+
test('different kinds of spans are independent', () => {
12774
registerCheckpoint('ttfd', SPAN_FIRST, 'a', true);
12875
registerCheckpoint('ttid', SPAN_FIRST, 'a', false);
12976
flushDefer();
13077
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(true);
13178
expect(isAllReady('ttid', SPAN_FIRST)).toBe(false);
13279
});
13380

134-
test('updateCheckpoint is a no-op for unknown id', () => {
135-
const listener = jest.fn();
136-
subscribe('ttfd', SPAN_FIRST, listener);
137-
updateCheckpoint('ttfd', SPAN_FIRST, 'nope', true);
138-
expect(listener).not.toHaveBeenCalled();
139-
});
140-
141-
test('updateCheckpoint with same ready value does not notify', () => {
142-
registerCheckpoint('ttfd', SPAN_FIRST, 'a', true);
143-
const listener = jest.fn();
144-
subscribe('ttfd', SPAN_FIRST, listener);
145-
updateCheckpoint('ttfd', SPAN_FIRST, 'a', true);
146-
expect(listener).not.toHaveBeenCalled();
147-
});
148-
14981
test('subscribers are notified only on aggregate-ready flips', () => {
15082
const listener = jest.fn();
15183
subscribe('ttfd', SPAN_FIRST, listener);
@@ -159,25 +91,6 @@ describe('timeToDisplayCoordinator', () => {
15991
expect(listener).toHaveBeenCalledTimes(2);
16092
});
16193

162-
test('non-flipping checkpoint changes do not wake subscribers (storm avoidance)', () => {
163-
const listener = jest.fn();
164-
subscribe('ttfd', SPAN_FIRST, listener);
165-
166-
for (let i = 0; i < 10; i++) {
167-
registerCheckpoint('ttfd', SPAN_FIRST, `cp-${i}`, false);
168-
}
169-
expect(listener).toHaveBeenCalledTimes(0);
170-
171-
for (let i = 0; i < 9; i++) {
172-
updateCheckpoint('ttfd', SPAN_FIRST, `cp-${i}`, true);
173-
}
174-
expect(listener).toHaveBeenCalledTimes(0);
175-
176-
updateCheckpoint('ttfd', SPAN_FIRST, 'cp-9', true);
177-
flushDefer();
178-
expect(listener).toHaveBeenCalledTimes(1);
179-
});
180-
18194
test('unsubscribe stops further notifications', () => {
18295
const listener = jest.fn();
18396
const unsubscribe = subscribe('ttfd', SPAN_FIRST, listener);
@@ -193,68 +106,25 @@ describe('timeToDisplayCoordinator', () => {
193106
expect(listener).not.toHaveBeenCalled();
194107
});
195108

196-
test('up-flip is deferred so a same-task late-mounting peer can cancel it', () => {
197-
// The defining race: header registers ready=true alone, sidebar mounts
198-
// a tick later (e.g. parent useEffect setState→child mount). Without the
199-
// defer, header would fire instantly. With the defer, sidebar's
200-
// registration cancels the pending up-flip before it fires.
109+
test('up-flip is deferred', () => {
201110
registerCheckpoint('ttfd', SPAN_FIRST, 'header', true);
202-
// Aggregate is *raw* ready, but `isAllReady` (deferred view) is still false.
203111
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(false);
204112

205-
// Same-task: sidebar mounts before the timer macrotask runs.
206113
registerCheckpoint('ttfd', SPAN_FIRST, 'sidebar', false);
207114

208115
flushDefer();
209-
// Aggregate must NOT have flipped — the defer protected us.
210116
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(false);
211-
212-
// Sidebar resolves — now we get a real ready transition.
213117
updateCheckpoint('ttfd', SPAN_FIRST, 'sidebar', true);
214118
flushDefer();
215119
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(true);
216120
});
217121

218-
test('down-flip is immediate (cancels pending up-flip and not deferred itself)', () => {
122+
test('down-flip is immediate', () => {
219123
registerCheckpoint('ttfd', SPAN_FIRST, 'a', true);
220124
flushDefer();
221125
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(true);
222126

223127
registerCheckpoint('ttfd', SPAN_FIRST, 'b', false);
224-
// No flushDefer: down-flip is immediate.
225128
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(false);
226129
});
227-
228-
test('clearSpan drops all coordinator state for a span', () => {
229-
// Simulates the integration calling clearSpan after popTimeToDisplayFor
230-
// returns. Prevents the registries from accumulating entries for screens
231-
// that outlive their span (keep-alive, idle-timeout discarded txns, etc.).
232-
registerCheckpoint('ttfd', SPAN_FIRST, 'a', true);
233-
registerCheckpoint('ttid', SPAN_FIRST, 'a', true);
234-
registerCheckpoint('ttfd', SPAN_SECOND, 'a', true);
235-
flushDefer();
236-
expect(isAllReady('ttfd', SPAN_FIRST)).toBe(true);
237-
flushDefer();
238-
expect(isAllReady('ttid', SPAN_FIRST)).toBe(true);
239-
flushDefer();
240-
expect(isAllReady('ttfd', SPAN_SECOND)).toBe(true);
241-
242-
clearSpan(SPAN_FIRST);
243-
244-
expect(hasAnyCheckpoints('ttfd', SPAN_FIRST)).toBe(false);
245-
expect(hasAnyCheckpoints('ttid', SPAN_FIRST)).toBe(false);
246-
// Other spans untouched.
247-
flushDefer();
248-
expect(isAllReady('ttfd', SPAN_SECOND)).toBe(true);
249-
});
250-
251-
test('clearSpan also drops sticky checkpoints', () => {
252-
registerCheckpoint('ttfd', SPAN_FIRST, 'a', true);
253-
const unregisterB = registerCheckpoint('ttfd', SPAN_FIRST, 'b', false);
254-
unregisterB(); // becomes sticky
255-
expect(hasAnyCheckpoints('ttfd', SPAN_FIRST)).toBe(true);
256-
257-
clearSpan(SPAN_FIRST);
258-
expect(hasAnyCheckpoints('ttfd', SPAN_FIRST)).toBe(false);
259-
});
260130
});

0 commit comments

Comments
 (0)