Skip to content

Commit 43a9724

Browse files
authored
Merge with firebaseprivate (#150)
* add a Firestore emulator test * more work on Firestore emulator tests * automatically start and stop emulator * move testing libs to devdependencies * fix scripts * fix SuspenseWithPerf bug * Cloud Build POC * Fix * fix gitignore to exclude pub dir * update rxfire and make tests exit * uncomment publish stuff * fix build (not publishing) * add missing build step
1 parent 473760c commit 43a9724

File tree

8 files changed

+195
-12078
lines changed

8 files changed

+195
-12078
lines changed

.gitignore

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,12 @@ dist/
55
node_modules/
66

77
.vscode/*
8-
reactfire/**/*.js
9-
!reactfire/babel.config.js
108

11-
reactfire/**/*.jsx
12-
reactfire/**/*.js.map
13-
reactfire/**/*.jsx.map
14-
reactfire/**/*.d.ts
15-
reactfire/pub/reactfire/package.json
9+
10+
1611
reactfire/docs/reactfire-metadata.json
1712
sample-complex/.cache/**
1813
sample-complex/src/firebase-key.json
19-
20-
pub
14+
reactfire/firestore-debug.log
15+
reactfire/pub/**
16+
pub
Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,117 @@
11
import { renderHook, act } from '@testing-library/react-hooks';
2+
import { render, waitForElement, cleanup } from '@testing-library/react';
3+
24
import * as React from 'react';
35
import '@testing-library/jest-dom/extend-expect';
6+
import * as firebase from '@firebase/testing';
7+
import {
8+
useFirestoreDoc,
9+
useFirestoreCollection,
10+
FirebaseAppProvider
11+
} from '..';
12+
import { firestore } from 'firebase/app';
413

514
describe('Firestore', () => {
15+
let app;
16+
17+
beforeAll(async () => {
18+
app = firebase.initializeTestApp({
19+
projectId: '12345',
20+
databaseName: 'my-database',
21+
auth: { uid: 'alice' }
22+
});
23+
});
24+
25+
afterEach(async () => {
26+
cleanup();
27+
await firebase.clearFirestoreData({ projectId: '12345' });
28+
});
29+
30+
test('sanity check - emulator is running', () => {
31+
// IF THIS TEST FAILS, MAKE SURE YOU'RE RUNNING THESE TESTS BY DOING:
32+
//
33+
34+
return app
35+
.firestore()
36+
.collection('test')
37+
.add({ a: 'hello' });
38+
});
39+
640
describe('useFirestoreDoc', () => {
7-
test.todo('returns the same value as ref.onSnapshot()');
41+
it('can get a Firestore document [TEST REQUIRES EMULATOR]', async () => {
42+
const mockData = { a: 'hello' };
43+
44+
const ref = app
45+
.firestore()
46+
.collection('testDoc')
47+
.doc('test1');
48+
49+
await ref.set(mockData);
50+
51+
const ReadFirestoreDoc = () => {
52+
const doc = useFirestoreDoc(
53+
(ref as unknown) as firestore.DocumentReference
54+
);
55+
56+
return (
57+
<h1 data-testid="readSuccess">
58+
{(doc as firestore.DocumentSnapshot).data().a}
59+
</h1>
60+
);
61+
};
62+
const { getByTestId } = render(
63+
<FirebaseAppProvider firebase={app}>
64+
<React.Suspense fallback={<h1 data-testid="fallback">Fallback</h1>}>
65+
<ReadFirestoreDoc />
66+
</React.Suspense>
67+
</FirebaseAppProvider>
68+
);
69+
70+
await waitForElement(() => getByTestId('readSuccess'));
71+
72+
expect(getByTestId('readSuccess')).toContainHTML(mockData.a);
73+
});
874
});
975

76+
// THIS TEST CAUSES A REACT `act` WARNING
77+
// IT WILL BE FIXED IN REACT 16.9
78+
// More info here: https://github.com/testing-library/react-testing-library/issues/281
1079
describe('useFirestoreCollection', () => {
11-
test.todo('returns the same value as ref.onSnapshot()');
80+
it('can get a Firestore collection [TEST REQUIRES EMULATOR]', async () => {
81+
const mockData1 = { a: 'hello' };
82+
const mockData2 = { a: 'goodbye' };
83+
84+
const ref = app.firestore().collection('testCollection');
85+
86+
await ref.add(mockData1);
87+
await ref.add(mockData2);
88+
89+
const ReadFirestoreCollection = () => {
90+
const collection = useFirestoreCollection(
91+
(ref as unknown) as firestore.CollectionReference
92+
);
93+
94+
return (
95+
<ul data-testid="readSuccess">
96+
{(collection as firestore.QuerySnapshot).docs.map(doc => (
97+
<li key={doc.id} data-testid="listItem">
98+
doc.data().a
99+
</li>
100+
))}
101+
</ul>
102+
);
103+
};
104+
const { getAllByTestId } = render(
105+
<FirebaseAppProvider firebase={app}>
106+
<React.Suspense fallback={<h1 data-testid="fallback">Fallback</h1>}>
107+
<ReadFirestoreCollection />
108+
</React.Suspense>
109+
</FirebaseAppProvider>
110+
);
111+
112+
await waitForElement(() => getAllByTestId('listItem'));
113+
114+
expect(getAllByTestId('listItem').length).toEqual(2);
115+
});
12116
});
13117
});

reactfire/package.json

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
"types": "index.d.ts",
77
"private": true,
88
"scripts": {
9-
"build-dev": "tsc",
9+
"build-dev": "tsc --watch",
1010
"test-dev": "jest --verbose --watch",
11-
"test": "jest --no-cache",
11+
"emulator": "firebase emulators:start --only firestore",
12+
"test": "firebase emulators:exec --only firestore \"jest --no-cache --verbose --detectOpenHandles --forceExit\"",
1213
"watch": "tsc --watch",
13-
"build": "rm -rf pub && tsc && cp package.pub.json pub/reactfire/package.json && cp ../README.md pub/reactfire/README.md"
14+
"build": "rm -rf pub && tsc && cp package.pub.json pub/reactfire/package.json && cp ../README.md pub/reactfire/README.md && cp ../LICENSE pub/reactfire/LICENSE"
1415
},
1516
"repository": {
1617
"type": "git",
@@ -23,7 +24,7 @@
2324
},
2425
"homepage": "https://firebaseopensource.com/projects/firebase/reactfire/",
2526
"dependencies": {
26-
"rxfire": "^3.6.3",
27+
"rxfire": "^3.6.6",
2728
"rxjs": "^6.4.0"
2829
},
2930
"devDependencies": {
@@ -33,13 +34,19 @@
3334
"@babel/preset-env": "^7.4.3",
3435
"@babel/preset-react": "^7.0.0",
3536
"@babel/preset-typescript": "^7.3.3",
37+
"@firebase/app": "^0.4.8",
38+
"@firebase/app-types": "^0.4.0",
39+
"@firebase/testing": "^0.11.4",
3640
"@testing-library/jest-dom": "^4.1.1",
3741
"@testing-library/react": "^9.3.0",
3842
"@testing-library/react-hooks": "^2.0.3",
3943
"@types/jest": "^24.0.11",
4044
"babel-jest": "^24.7.1",
45+
"firebase-tools": "^7.1.0",
4146
"jest": "~24.7.1",
47+
"jest-dom": "^3.1.3",
48+
"typescript": "^3.4.5",
49+
"firebase-functions-test": "^0.1.6"
4250
"react-test-renderer": "^16.9.0",
43-
"typescript": "^3.4.5"
4451
}
4552
}

reactfire/performance/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,15 @@ export function SuspenseWithPerf({
3535
firePerf
3636
}: SuspensePerfProps) {
3737
firePerf = firePerf || getPerfFromContext();
38-
const trace = React.useMemo(() => firePerf.trace(traceId), [traceId]);
38+
39+
const trace = React.useRef(firePerf.trace(traceId));
3940

4041
const Fallback = () => {
4142
React.useLayoutEffect(() => {
42-
trace.start();
43+
trace.current.start();
4344

4445
return () => {
45-
trace.stop();
46+
trace.current.stop();
4647
};
4748
}, []);
4849

reactfire/performance/performance.test.tsx

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import '@testing-library/jest-dom/extend-expect';
44
import * as React from 'react';
55
import { Subject } from 'rxjs';
66
import { SuspenseWithPerf } from '.';
7-
import { FirebaseAppProvider } from '../firebaseApp';
7+
import { FirebaseAppProvider, useObservable } from '..';
88

99
const traceStart = jest.fn();
1010
const 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
});

sample-simple/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"react-dom": "^16.8.6",
1010
"react-firebaseui": "^3.1.2",
1111
"react-scripts": "3.0.1",
12-
"reactfire": "2.0.0"
12+
"reactfire": "^2.0.0-alpha.2"
1313
},
1414
"scripts": {
1515
"start": "react-scripts start",

test.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/bash
2+
set -e
3+
4+
cd reactfire
5+
6+
yarn
7+
yarn build
8+
yarn test

0 commit comments

Comments
 (0)