diff --git a/packages/web-sdk/src/managers/EmbracePageManager/EmbracePageManager.test.ts b/packages/web-sdk/src/managers/EmbracePageManager/EmbracePageManager.test.ts index 424ea86ac..678a6c726 100644 --- a/packages/web-sdk/src/managers/EmbracePageManager/EmbracePageManager.test.ts +++ b/packages/web-sdk/src/managers/EmbracePageManager/EmbracePageManager.test.ts @@ -11,35 +11,16 @@ import { EmbracePageManager } from './EmbracePageManager.ts'; chai.use(sinonChai); const { expect } = chai; -class FakeNavigation { - private readonly _listeners: Array< - (event: NavigationCurrentEntryChangeEvent) => void - > = []; - - public addEventListener( - _type: 'currententrychange', - listener: (event: NavigationCurrentEntryChangeEvent) => void, - ): void { - this._listeners.push(listener); - } - - public removeEventListener( - _type: 'currententrychange', - listener: (event: NavigationCurrentEntryChangeEvent) => void, - ): void { - const i = this._listeners.indexOf(listener); - if (i !== -1) { - this._listeners.splice(i, 1); - } - } - - public fire(from: string): void { +class FakeNavigation extends EventTarget { + public fire(from: string, timeStamp = performance.now()): void { const event = Object.assign(new Event('currententrychange'), { from: { url: from }, }) as NavigationCurrentEntryChangeEvent; - for (const listener of [...this._listeners]) { - listener(event); - } + Object.defineProperty(event, 'timeStamp', { + value: timeStamp, + configurable: true, + }); + this.dispatchEvent(event); } } @@ -268,7 +249,7 @@ describe('EmbracePageManager', () => { const perf = new OTelPerformanceManager(); const zeroTimeBefore = perf.getZeroTime(); - navigation.fire('http://previous.example.com/'); + navigation.fire('http://previous.example.com/', performance.now() + 1000); expect(perf.getZeroTime()).to.be.greaterThan(zeroTimeBefore); }); diff --git a/packages/web-sdk/src/transport/FetchTransport/FetchTransport.test.ts b/packages/web-sdk/src/transport/FetchTransport/FetchTransport.test.ts index 981796f1a..c248f6b2e 100644 --- a/packages/web-sdk/src/transport/FetchTransport/FetchTransport.test.ts +++ b/packages/web-sdk/src/transport/FetchTransport/FetchTransport.test.ts @@ -1,5 +1,6 @@ import { DiagLogLevel, diag } from '@opentelemetry/api'; import * as chai from 'chai'; +import * as sinon from 'sinon'; import { fakeFetchGetKeepalive, fakeFetchInstall, @@ -386,6 +387,45 @@ describe('FetchTransport', () => { expect(result.status).to.equal('retryable'); }); + + it('should return failure when fetch throws a non-retryable error', async () => { + reinstallFetch().rejects(new Error('Some other network error')); + + const transport = makeTransport(); + const result = await transport.send(smallPayload, 1000); + + expect(result.status).to.equal('failure'); + }); + }); + + describe('AbortSignal.timeout fallback', () => { + let originalTimeout: typeof AbortSignal.timeout | undefined; + + beforeEach(() => { + // Simulates a browser without AbortSignal.timeout, forcing the + // AbortController + setTimeout fallback path. + originalTimeout = AbortSignal.timeout; + // @ts-expect-error deleting a normally-present static method + delete AbortSignal.timeout; + }); + + afterEach(() => { + AbortSignal.timeout = originalTimeout as typeof AbortSignal.timeout; + }); + + it('should clear the fallback timer once the request completes', async () => { + fakeFetchRespondWith('ok', { status: 200 }); + const clearTimeoutSpy = sinon.spy(globalThis, 'clearTimeout'); + + try { + const transport = makeTransport(); + await transport.send(smallPayload, 1000); + + void expect(clearTimeoutSpy.calledOnce).to.be.true; + } finally { + clearTimeoutSpy.restore(); + } + }); }); describe('HTTP status classification', () => { @@ -466,6 +506,17 @@ describe('FetchTransport', () => { expect(match).to.be.a('string'); }); + it('should warn via diag when fetch throws a non-retryable error', async () => { + reinstallFetch().rejects(new Error('Some other network error')); + + const transport = makeTransport(); + await transport.send(smallPayload, 1000); + + const warns = diagLogger.getWarnLogs(); + const match = warns.find((msg) => msg.includes('Fetch transport failed')); + expect(match).to.be.a('string'); + }); + it('should log debug when HTTP response is 5xx', async () => { fakeFetchRespondWith('error', { status: 500 }); const transport = makeTransport();