From eee70919c65d37c0d3154371191b1a581f9deaa3 Mon Sep 17 00:00:00 2001 From: Rup Sarmah <84681565+sarmah-rup@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:39:27 +0530 Subject: [PATCH] (fix) tracker-js: apply error sampleRate the right way round captureError skipped an error when sampleRate >= Math.random(), so sampleRate: 1 dropped every error and sampleRate: 0 sent all of them. Skip when Math.random() >= sampleRate instead, which matches the documented meaning (share of errors to send). --- packages/tracker-js/src/Lib.ts | 2 +- .../tracker-js/tests/errorSampling.test.ts | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 packages/tracker-js/tests/errorSampling.test.ts diff --git a/packages/tracker-js/src/Lib.ts b/packages/tracker-js/src/Lib.ts index 2cb44c59e..07af3996e 100644 --- a/packages/tracker-js/src/Lib.ts +++ b/packages/tracker-js/src/Lib.ts @@ -400,7 +400,7 @@ export class Lib { } captureError(event: ErrorEvent): void { - if (typeof this.errorsOptions?.sampleRate === 'number' && this.errorsOptions.sampleRate >= Math.random()) { + if (typeof this.errorsOptions?.sampleRate === 'number' && Math.random() >= this.errorsOptions.sampleRate) { return } diff --git a/packages/tracker-js/tests/errorSampling.test.ts b/packages/tracker-js/tests/errorSampling.test.ts new file mode 100644 index 000000000..95ea2bd0e --- /dev/null +++ b/packages/tracker-js/tests/errorSampling.test.ts @@ -0,0 +1,61 @@ +import { Lib } from '../src/Lib' +import { setLocation } from './testUtils' + +describe('Error sampling', () => { + let lib: Lib + let sendRequest: jest.SpyInstance + + const errorEvent = () => + new ErrorEvent('error', { + message: 'Boom', + filename: 'app.js', + lineno: 1, + colno: 1, + error: new Error('Boom'), + }) + + beforeEach(() => { + setLocation({ pathname: '/home' }) + lib = new Lib('test-project', { devMode: true }) + sendRequest = jest.spyOn(lib as any, 'sendRequest').mockResolvedValue(undefined) + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + test('sends every error when sampleRate is 1', () => { + const actions = lib.trackErrors({ sampleRate: 1 }) + jest.spyOn(Math, 'random').mockReturnValue(0.5) + + lib.captureError(errorEvent()) + actions.stop() + + expect(sendRequest).toHaveBeenCalledWith('error', expect.objectContaining({ name: 'Error', message: 'Boom' })) + }) + + test('sends no errors when sampleRate is 0', () => { + const actions = lib.trackErrors({ sampleRate: 0 }) + jest.spyOn(Math, 'random').mockReturnValue(0.5) + + lib.captureError(errorEvent()) + actions.stop() + + expect(sendRequest).not.toHaveBeenCalled() + }) + + test('keeps roughly sampleRate share of errors', () => { + const actions = lib.trackErrors({ sampleRate: 0.2 }) + const random = jest.spyOn(Math, 'random') + + random.mockReturnValue(0.1) + lib.captureError(errorEvent()) + expect(sendRequest).toHaveBeenCalledTimes(1) + + random.mockReturnValue(0.9) + lib.captureError(errorEvent()) + expect(sendRequest).toHaveBeenCalledTimes(1) + + actions.stop() + }) +})