@@ -4,7 +4,7 @@ import '@testing-library/jest-dom/extend-expect';
44import * as React from 'react' ;
55import { Subject } from 'rxjs' ;
66import { SuspenseWithPerf } from '.' ;
7- import { FirebaseAppProvider } from '../firebaseApp ' ;
7+ import { FirebaseAppProvider , useObservable } from '..' ;
88
99const traceStart = jest . fn ( ) ;
1010const traceEnd = jest . fn ( ) ;
@@ -36,21 +36,11 @@ describe('SuspenseWithPerf', () => {
3636
3737 it ( 'behaves the same as Suspense (render fallback until thrown promise resolves)' , async ( ) => {
3838 const o$ = new Subject ( ) ;
39- let shouldThrow = true ;
40-
41- const promise = new Promise ( ( resolve , reject ) => {
42- o$ . subscribe ( ( ) => {
43- shouldThrow = false ;
44- resolve ( ) ;
45- } ) ;
46- } ) ;
4739
4840 const Fallback = ( ) => < h1 data-testid = "fallback" > Fallback</ h1 > ;
4941
5042 const Comp = ( ) => {
51- if ( shouldThrow ) {
52- throw promise ;
53- }
43+ useObservable ( o$ , 'test' ) ;
5444
5545 return < h1 data-testid = "child" > Actual</ h1 > ;
5646 } ;
@@ -88,8 +78,6 @@ describe('SuspenseWithPerf', () => {
8878
8979 expect ( queryAllByTestId ( 'fallback' ) . length ) . toEqual ( 0 ) ;
9080 expect ( queryAllByTestId ( 'child' ) . length ) . toEqual ( 2 ) ;
91-
92- // expect(suspense.innerHTML).toEqual('Fallback');
9381 } ) ;
9482
9583 it ( 'creates a trace with the correct name' , ( ) => {
@@ -172,4 +160,61 @@ describe('SuspenseWithPerf', () => {
172160
173161 expect ( createTrace ) . toHaveBeenCalled ( ) ;
174162 } ) ;
163+
164+ it ( 'Does not reuse a trace object' , async ( ) => {
165+ // traces throw if you call start() twice,
166+ // even if you've called stop() in between:
167+ // https://github.com/firebase/firebase-js-sdk/blob/dd098c6a87f23ddf54a7f9b21b87f7bb3fd56bdd/packages/performance/src/resources/trace.test.ts#L52
168+ //
169+ // this test covers the scenario where a component
170+ // is rendered, and uses suspenseWithPerf, is then
171+ // hidden, and then re-rendered, triggering another
172+ // suspenseWithPerf
173+ //
174+ // I ran into this when I loaded a site while logged
175+ // in, rendering a component that used a reactfire hook,
176+ // then logged out, hiding that component. When I logged
177+ // back in without reloading the page, perfmon threw an error
178+ // because SuspenseWithPerf tried to reuse a trace.
179+
180+ const o$ = new Subject ( ) ;
181+
182+ const Comp = ( ) => {
183+ useObservable ( o$ , 'test' ) ;
184+
185+ return < h1 data-testid = "child" > Actual</ h1 > ;
186+ } ;
187+
188+ const Component = ( { renderPerf } ) => {
189+ if ( renderPerf ) {
190+ return (
191+ < SuspenseWithPerf
192+ traceId = { 'hello' }
193+ fallback = { 'loading' }
194+ firePerf = { ( mockPerf ( ) as unknown ) as performance . Performance }
195+ >
196+ < Comp />
197+ </ SuspenseWithPerf >
198+ ) ;
199+ } else {
200+ return < div data-testid = "other-element" > no perf</ div > ;
201+ }
202+ } ;
203+
204+ // render SuspenseWithPerf and go through normal trace start -> trace stop
205+ const { getByTestId, rerender } = render ( < Component renderPerf /> ) ;
206+ expect ( createTrace ) . toHaveBeenCalledTimes ( 1 ) ;
207+ act ( ( ) => o$ . next ( 'some value' ) ) ;
208+ await waitForElement ( ( ) => getByTestId ( 'child' ) ) ;
209+
210+ // re-render with changed props. now we're not rendering SuspenseWithPerf any more
211+ rerender ( < Component renderPerf = { false } /> ) ;
212+ await waitForElement ( ( ) => getByTestId ( 'other-element' ) ) ;
213+
214+ // re-render with changed props to render SuspenseWithPerf again
215+ rerender ( < Component renderPerf /> ) ;
216+
217+ // if createTrace is only called once, the firebase SDK will throw
218+ expect ( createTrace ) . toHaveBeenCalledTimes ( 2 ) ;
219+ } ) ;
175220} ) ;
0 commit comments