Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion config/webpack.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ module.exports = {
resolve: {
modules: ['node_modules', path.resolve(__dirname, '../../node_modules')],
mainFields: ['browser', 'module', 'main'],
extensions: ['.js', '.ts'],
extensions: ['.js', '.ts', '.tsx'],
symlinks: true
},
plugins: [
Expand Down
31 changes: 0 additions & 31 deletions docs-devsite/telemetry.instrumentation.md

This file was deleted.

6 changes: 0 additions & 6 deletions docs-devsite/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,6 @@ https://github.com/firebase/firebase-js-sdk
| --- | --- |
| [Telemetry](./telemetry.telemetry.md#telemetry_interface) | An instance of the Firebase Telemetry SDK.<!-- -->Do not create this instance directly. Instead, use [getTelemetry()](./telemetry.md#gettelemetry_cf608e1)<!-- -->. |

## Namespaces

| Namespace | Description |
| --- | --- |
| [Instrumentation](./telemetry.instrumentation.md#instrumentation_namespace) | |

## Variables

| Variable | Description |
Expand Down
17 changes: 17 additions & 0 deletions packages/telemetry/api-extractor.react.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"extends": "../../config/api-extractor.json",
"mainEntryPointFilePath": "<projectFolder>/dist/react/src/react/index.d.ts",
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "<projectFolder>/dist/react/index.d.ts"
},
"apiReport": {
"enabled": false
},
"docModel": {
"enabled": false
},
"tsdocMetadata": {
"enabled": false
}
}
5 changes: 4 additions & 1 deletion packages/telemetry/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@

const karmaBase = require('../../config/karma.base');

const files = [`src/**/*.test.ts`];
const files = [`src/**/*.test.ts*`];

module.exports = function (config) {
const karmaConfig = {
...karmaBase,
files,
preprocessors: {
'src/**/*.test.ts*': ['webpack', 'sourcemap']
},
frameworks: ['mocha']
};

Expand Down
35 changes: 32 additions & 3 deletions packages/telemetry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
},
"default": "./dist/index.esm.js"
},
"./react": {
"types": "./dist/react/index.d.ts",
"node": {
"import": "./dist/react/index.node.esm.js",
"default": "./dist/react/index.node.cjs.js"
},
"browser": {
"require": "./dist/react/index.cjs.js",
"import": "./dist/react/index.esm.js"
},
"default": "./dist/react/index.esm.js"
},
"./package.json": "./package.json"
},
"files": [
Expand All @@ -34,14 +46,26 @@
"test:ci": "node ../../scripts/run_tests_in_ci.js -s test:all",
"test:all": "run-p --npm-path npm test:browser test:node",
"test:browser": "karma start",
"test:node": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha src/**/*.test.* --config ../../config/mocharc.node.js",
"test:node": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha **/*.test.ts src/**/*.test.ts --config ../../config/mocharc.node.js",
"trusted-type-check": "tsec -p tsconfig.json --noEmit",
"api-report": "api-extractor run --local --verbose",
"api-report": "yarn api-report:main && yarn api-report:react",
"api-report:main": "api-extractor run --local --verbose",
"api-report:react": "api-extractor run --config api-extractor.react.json --local --verbose",
"typings:public": "node ../../scripts/build/use_typings.js ./dist/telemetry-public.d.ts"
},
"peerDependencies": {
"@firebase/app": "0.x",
"@firebase/app-types": "0.x"
"@firebase/app-types": "0.x",
"@types/react": "17.x || 18.x || 19.x",
"react": "17.x || 18.x || 19.x"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"@types/react": {
"optional": true
}
},
"dependencies": {
"@firebase/component": "0.7.0",
Expand All @@ -60,7 +84,12 @@
"@firebase/app": "0.14.2",
"@opentelemetry/sdk-trace-web": "2.1.0",
"@rollup/plugin-json": "6.1.0",
"@testing-library/dom": "10.4.1",
"@testing-library/react": "16.3.0",
"@types/react": "19.1.13",
"next": "15.5.2",
"react": "19.1.1",
"react-dom": "19.1.1",
"rollup": "2.79.2",
"rollup-plugin-replace": "2.2.0",
"rollup-plugin-typescript2": "0.36.0",
Expand Down
84 changes: 82 additions & 2 deletions packages/telemetry/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const browserBuilds = [
{
input: 'index.ts',
output: {
file: './dist/index.cjs.js',
file: pkg.exports['.'].browser.require,
format: 'cjs',
sourcemap: true
},
Expand Down Expand Up @@ -73,4 +73,84 @@ const nodeBuilds = [
}
];

export default [...browserBuilds, ...nodeBuilds];
const reactBuilds = [
{
input: 'src/react/index.ts',
output: {
file: pkg.exports['./react'].browser.import,
format: 'es',
sourcemap: true,
banner: `'use client';`
},
plugins: [
typescriptPlugin({
typescript,
tsconfig: 'tsconfig.react.json'
}),
json()
],
external: id => deps.some(dep => id === dep || id.startsWith(`${dep}/`))
},
{
input: 'src/react/index.ts',
output: {
file: pkg.exports['./react'].browser.require,
format: 'cjs',
sourcemap: true,
banner: `'use client';`
},
plugins: [
typescriptPlugin({
typescript,
tsconfig: 'tsconfig.react.json'
}),
json()
],
external: id => deps.some(dep => id === dep || id.startsWith(`${dep}/`))
}
];

const reactNodeBuilds = [
{
input: 'src/react/index.ts',
output: {
file: pkg.exports['./react'].node.default,
format: 'cjs',
sourcemap: true,
banner: `'use client';`
},
plugins: [
typescriptPlugin({
typescript,
tsconfig: 'tsconfig.react.json'
}),
json()
],
external: id => deps.some(dep => id === dep || id.startsWith(`${dep}/`))
},
{
input: 'src/react/index.ts',
output: {
file: pkg.exports['./react'].node.import,
format: 'es',
sourcemap: true,
banner: `'use client';`
},
plugins: [
typescriptPlugin({
typescript,
tsconfig: 'tsconfig.react.json'
}),
json(),
emitModulePackageFile()
],
external: id => deps.some(dep => id === dep || id.startsWith(`${dep}/`))
}
];

export default [
...browserBuilds,
...nodeBuilds,
...reactBuilds,
...reactNodeBuilds
];
111 changes: 111 additions & 0 deletions packages/telemetry/src/react/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* @license
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { expect, use } from 'chai';
import sinonChai from 'sinon-chai';
import chaiAsPromised from 'chai-as-promised';
import { restore, stub } from 'sinon';
import * as app from '@firebase/app';
import * as telemetry from '../api';
import { FirebaseApp } from '@firebase/app';
import { Telemetry } from '../public-types';
import { FirebaseTelemetry } from '.';
import React from 'react';
import { render } from '@testing-library/react';

use(sinonChai);
use(chaiAsPromised);

describe('FirebaseTelemetry', () => {
let getTelemetryStub: sinon.SinonStub;
let captureErrorStub: sinon.SinonStub;
let initializeAppStub: 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');
});

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

it('gets telemetry with the default app if no firebaseOptions are provided', () => {
render(<FirebaseTelemetry />);
expect(getTelemetryStub).to.have.been.called;
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);
expect(getTelemetryStub).to.have.been.calledWith(fakeApp);
});

it('captures window errors', done => {
render(<FirebaseTelemetry />);
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(captureErrorStub).to.have.been.calledWith(fakeTelemetry, error);
done();
});
window.dispatchEvent(new ErrorEvent('error', { error }));
});

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

it('renders children', () => {
const { getByText } = render(
<FirebaseTelemetry>
<span>here are my children</span>
</FirebaseTelemetry>
);
expect(getByText('here are my children')).to.exist;
});

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

expect(() => render(<FirebaseTelemetry />)).not.to.throw();
expect(consoleWarnStub).to.have.been.calledWith(
'Firebase Telemetry was not initialized:\n',
error
);
});
});
Loading
Loading