Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions common/api-review/telemetry-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
```ts

import { FirebaseApp } from '@firebase/app';
import { FirebaseOptions } from '@firebase/app';
import { LoggerProvider } from '@opentelemetry/sdk-logs';

// @public
export function FirebaseTelemetry({ firebaseOptions, telemetryOptions }: {
firebaseOptions?: FirebaseOptions;
export function FirebaseTelemetry({ firebaseApp, telemetryOptions }: {
firebaseApp: FirebaseApp;
telemetryOptions?: TelemetryOptions;
}): null;

Expand Down
14 changes: 7 additions & 7 deletions docs-devsite/telemetry_react.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ https://github.com/firebase/firebase-js-sdk

| Function | Description |
| --- | --- |
| [FirebaseTelemetry({ firebaseOptions, telemetryOptions })](./telemetry_react.md#firebasetelemetry_f37eb31) | Registers event listeners for uncaught errors.<!-- -->This should be installed near the root of your application. Caught errors, including those implicitly caught by Error Boundaries, will not be captured by this component. |
| [FirebaseTelemetry({ firebaseApp, telemetryOptions })](./telemetry_react.md#firebasetelemetry_10424e8) | Registers event listeners for uncaught errors.<!-- -->This should be installed near the root of your application. Caught errors, including those implicitly caught by Error Boundaries, will not be captured by this component. |

## Interfaces

Expand All @@ -24,9 +24,9 @@ https://github.com/firebase/firebase-js-sdk
| [Telemetry](./telemetry_react.telemetry.md#telemetry_interface) | An instance of the Firebase Telemetry SDK.<!-- -->Do not create this instance directly. Instead, use [getTelemetry()](./telemetry_.md#gettelemetry_448bdc6)<!-- -->. |
| [TelemetryOptions](./telemetry_react.telemetryoptions.md#telemetryoptions_interface) | Options for initialized the Telemetry service using [getTelemetry()](./telemetry_.md#gettelemetry_448bdc6)<!-- -->. |

## function({ firebaseOptions, telemetryOptions }, ...)
## function({ firebaseApp, telemetryOptions }, ...)

### FirebaseTelemetry({ firebaseOptions, telemetryOptions }) {:#firebasetelemetry_f37eb31}
### FirebaseTelemetry({ firebaseApp, telemetryOptions }) {:#firebasetelemetry_10424e8}

Registers event listeners for uncaught errors.

Expand All @@ -35,8 +35,8 @@ This should be installed near the root of your application. Caught errors, inclu
<b>Signature:</b>

```typescript
export declare function FirebaseTelemetry({ firebaseOptions, telemetryOptions }: {
firebaseOptions?: FirebaseOptions;
export declare function FirebaseTelemetry({ firebaseApp, telemetryOptions }: {
firebaseApp: FirebaseApp;
telemetryOptions?: TelemetryOptions;
}): null;
```
Expand All @@ -45,7 +45,7 @@ export declare function FirebaseTelemetry({ firebaseOptions, telemetryOptions }:

| Parameter | Type | Description |
| --- | --- | --- |
| { firebaseOptions, telemetryOptions } | { firebaseOptions?: [FirebaseOptions](./app.firebaseoptions.md#firebaseoptions_interface)<!-- -->; telemetryOptions?: [TelemetryOptions](./telemetry_.telemetryoptions.md#telemetryoptions_interface)<!-- -->; } | |
| { firebaseApp, telemetryOptions } | { firebaseApp: [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface)<!-- -->; telemetryOptions?: [TelemetryOptions](./telemetry_.telemetryoptions.md#telemetryoptions_interface)<!-- -->; } | |

<b>Returns:</b>

Expand All @@ -58,7 +58,7 @@ The default [Telemetry](./telemetry_.telemetry.md#telemetry_interface) instance

```html
<body>
<FirebaseTelemetry firebaseOptions={options} />
<FirebaseTelemetry firebaseApp={app} />
... my app ...
</body>

Expand Down
35 changes: 4 additions & 31 deletions packages/telemetry/src/react/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,72 +33,45 @@ use(chaiAsPromised);
describe('FirebaseTelemetry', () => {
let getTelemetryStub: sinon.SinonStub;
let captureErrorStub: sinon.SinonStub;
let initializeAppStub: sinon.SinonStub;
let getAppStub: sinon.SinonStub;
let fakeApp: FirebaseApp;
let fakeTelemetry: Telemetry;

beforeEach(() => {
fakeApp = { name: 'fakeApp' } as FirebaseApp;
fakeTelemetry = {} as Telemetry;

initializeAppStub = stub(app, 'initializeApp').returns(fakeApp);
getTelemetryStub = stub(telemetry, 'getTelemetry').returns(fakeTelemetry);
captureErrorStub = stub(telemetry, 'captureError');
getAppStub = stub(app, 'getApp').returns(fakeApp);
});

afterEach(() => {
restore();
});

it('gets telemetry with the default app if no firebaseOptions are provided', () => {
render(<FirebaseTelemetry />);
expect(initializeAppStub).not.to.have.been.called;
});

it('initializes a new app and gets telemetry if firebaseOptions are provided', () => {
const firebaseOptions = { apiKey: 'test' };
render(<FirebaseTelemetry firebaseOptions={firebaseOptions} />);
expect(initializeAppStub).to.have.been.calledWith(firebaseOptions);
});

it('captures window errors', done => {
render(<FirebaseTelemetry />);
render(<FirebaseTelemetry firebaseApp={fakeApp} />);
const error = new Error('test error');
window.onerror = () => {
// Prevent error from bubbling up to test suite
};
window.addEventListener('error', (event: ErrorEvent) => {
// Registers another listener (sequential) to confirm behaviour.
expect(getTelemetryStub).to.have.been.called;
expect(getTelemetryStub).to.have.been.calledWith(fakeApp);
expect(captureErrorStub).to.have.been.calledWith(fakeTelemetry, error);
done();
});
window.dispatchEvent(new ErrorEvent('error', { error }));
});

it('captures unhandled promise rejections', () => {
render(<FirebaseTelemetry />);
render(<FirebaseTelemetry firebaseApp={fakeApp} />);
const reason = new Error('test rejection');
const promise = Promise.reject(reason);
promise.catch(() => {});
window.dispatchEvent(
new PromiseRejectionEvent('unhandledrejection', { reason, promise })
);
expect(getTelemetryStub).to.have.been.called;
expect(getTelemetryStub).to.have.been.calledWith(fakeApp);
expect(captureErrorStub).to.have.been.calledWith(fakeTelemetry, reason);
});

it('fails silently when getTelemetry fails', () => {
const error = new Error('getTelemetry failed');
initializeAppStub.throws(error);
const consoleWarnStub = stub(console, 'warn');

expect(() => render(<FirebaseTelemetry firebaseOptions={{}}/>)).not.to.throw();
expect(consoleWarnStub).to.have.been.calledWith(
'Firebase Telemetry was not initialized:\n',
error
);
});
});
27 changes: 16 additions & 11 deletions packages/telemetry/src/react/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

import { FirebaseOptions, getApp, initializeApp } from '@firebase/app';
import { FirebaseApp } from '@firebase/app';
import { registerTelemetry } from '../register';
import { captureError, getTelemetry } from '../api';
import { TelemetryOptions } from '../public-types';
Expand All @@ -34,22 +34,22 @@ export * from '../public-types';
* @example
* ```html
* <body>
* <FirebaseTelemetry firebaseOptions={options} />
* <FirebaseTelemetry firebaseApp={app} />
* ... my app ...
* </body>
* ```
*
* @param firebaseOptions - Options to run {@link @firebase/app#initializeApp}. If this is not provided, initializeApp needs to be called explicitly elsewhere in your application.
* @param firebaseApp - The {@link @firebase/app#FirebaseApp} instance to use.
* @param telemetryOptions - {@link TelemetryOptions} that configure the Telemetry instance.
* @returns The default {@link Telemetry} instance for the given {@link @firebase/app#FirebaseApp}.
*
* @public
*/
export function FirebaseTelemetry({
firebaseOptions,
firebaseApp,
telemetryOptions
}: {
firebaseOptions?: FirebaseOptions;
firebaseApp: FirebaseApp;
telemetryOptions?: TelemetryOptions;
}): null {
useEffect(() => {
Expand All @@ -58,17 +58,22 @@ export function FirebaseTelemetry({
}

const errorListener = (event: ErrorEvent): void => {
captureError(getTelemetry(getApp(), telemetryOptions), event.error, {});
captureError(
getTelemetry(firebaseApp, telemetryOptions),
event.error,
{}
);
};

const unhandledRejectionListener = (event: PromiseRejectionEvent): void => {
captureError(getTelemetry(getApp(), telemetryOptions), event.reason, {});
captureError(
getTelemetry(firebaseApp, telemetryOptions),
event.reason,
{}
);
};

try {
if (firebaseOptions) {
initializeApp(firebaseOptions);
}
window.addEventListener('error', errorListener);
window.addEventListener('unhandledrejection', unhandledRejectionListener);
} catch (error) {
Expand All @@ -82,7 +87,7 @@ export function FirebaseTelemetry({
unhandledRejectionListener
);
};
}, []);
}, [firebaseApp, telemetryOptions]);

return null;
}
Loading