From 3f74f823fc2a47d0ed015cf1b70d862254d97c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Og=C3=B3rek?= Date: Thu, 9 Sep 2021 14:41:10 +0200 Subject: [PATCH] ref(browser): Migrate unit tests from Chai and Karma to Jest (#3965) --- packages/browser/.eslintrc.js | 2 + packages/browser/package.json | 4 +- packages/browser/test/unit/backend.test.ts | 6 +- packages/browser/test/unit/index.test.ts | 125 +++--- .../test/unit/integrations/helpers.test.ts | 91 +++-- .../unit/integrations/linkederrors.test.ts | 66 ++-- packages/browser/test/unit/jest.config.js | 14 + packages/browser/test/unit/karma.conf.js | 43 --- packages/browser/test/unit/parsers.test.ts | 32 +- .../browser/test/unit/tracekit/custom.test.ts | 40 +- .../test/unit/tracekit/original.test.ts | 322 ++++++++-------- .../browser/test/unit/transports/base.test.ts | 10 +- .../test/unit/transports/fetch.test.ts | 364 ++++++++---------- .../browser/test/unit/transports/xhr.test.ts | 257 ++++++------- packages/browser/tsconfig.json | 2 +- 15 files changed, 634 insertions(+), 744 deletions(-) create mode 100644 packages/browser/test/unit/jest.config.js delete mode 100644 packages/browser/test/unit/karma.conf.js diff --git a/packages/browser/.eslintrc.js b/packages/browser/.eslintrc.js index 991c8b028d6a..ff67d52a9fc6 100644 --- a/packages/browser/.eslintrc.js +++ b/packages/browser/.eslintrc.js @@ -25,6 +25,8 @@ module.exports = { 'prefer-template': 'off', 'no-unused-expressions': 'off', 'guard-for-in': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', }, }, { diff --git a/packages/browser/package.json b/packages/browser/package.json index a0818abc994c..7a7330da838e 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -73,8 +73,8 @@ "fix:prettier": "prettier --write \"{src,test}/**/*.ts\"", "fix:eslint": "eslint . --format stylish --fix", "test": "run-s test:unit", - "test:unit": "karma start test/unit/karma.conf.js", - "test:unit:watch": "karma start test/unit/karma.conf.js --auto-watch --no-single-run", + "test:unit": "jest --config test/unit/jest.config.js", + "test:unit:watch": "jest --config test/unit/jest.config.js --watch", "test:integration": "test/integration/run.js", "test:integration:watch": "test/integration/run.js --watch", "test:integration:checkbrowsers": "node scripts/checkbrowsers.js", diff --git a/packages/browser/test/unit/backend.test.ts b/packages/browser/test/unit/backend.test.ts index 5477dc27a567..71ffec4d75b1 100644 --- a/packages/browser/test/unit/backend.test.ts +++ b/packages/browser/test/unit/backend.test.ts @@ -1,14 +1,12 @@ -import { expect } from 'chai'; - import { BrowserBackend } from '../../src/backend'; let backend: BrowserBackend; describe('BrowserBackend', () => { describe('sendEvent()', () => { - it('should use NoopTransport', async () => { + it('should use NoopTransport', () => { backend = new BrowserBackend({}); - expect(backend.getTransport().constructor.name).to.equal('NoopTransport'); + expect(backend.getTransport().constructor.name).toBe('NoopTransport'); }); }); }); diff --git a/packages/browser/test/unit/index.test.ts b/packages/browser/test/unit/index.test.ts index 0a4b1a01de4b..f5f4554c552a 100644 --- a/packages/browser/test/unit/index.test.ts +++ b/packages/browser/test/unit/index.test.ts @@ -1,6 +1,4 @@ import { SDK_VERSION } from '@sentry/core'; -import { expect } from 'chai'; -import { SinonSpy, spy } from 'sinon'; import { addBreadcrumb, @@ -22,13 +20,13 @@ import { SimpleTransport } from './mocks/simpletransport'; const dsn = 'https://53039209a22b4ec1bcc296a3c9fdecd6@sentry.io/4291'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any, no-var +// eslint-disable-next-line no-var declare var global: any; describe('SentryBrowser', () => { - const beforeSend: SinonSpy<[Event], Event> = spy((event: Event) => event); + const beforeSend = jest.fn(); - before(() => { + beforeAll(() => { init({ beforeSend, dsn, @@ -42,7 +40,7 @@ describe('SentryBrowser', () => { afterEach(() => { getCurrentHub().popScope(); - beforeSend.resetHistory(); + beforeSend.mockReset(); }); describe('getContext() / setContext()', () => { @@ -50,7 +48,7 @@ describe('SentryBrowser', () => { configureScope((scope: Scope) => { scope.setExtra('abc', { def: [1] }); }); - expect(global.__SENTRY__.hub._stack[1].scope._extra).to.deep.equal({ + expect(global.__SENTRY__.hub._stack[1].scope._extra).toEqual({ abc: { def: [1] }, }); }); @@ -59,7 +57,7 @@ describe('SentryBrowser', () => { configureScope((scope: Scope) => { scope.setTag('abc', 'def'); }); - expect(global.__SENTRY__.hub._stack[1].scope._tags).to.deep.equal({ + expect(global.__SENTRY__.hub._stack[1].scope._tags).toEqual({ abc: 'def', }); }); @@ -68,7 +66,7 @@ describe('SentryBrowser', () => { configureScope((scope: Scope) => { scope.setUser({ id: 'def' }); }); - expect(global.__SENTRY__.hub._stack[1].scope._user).to.deep.equal({ + expect(global.__SENTRY__.hub._stack[1].scope._user).toEqual({ id: 'def', }); }); @@ -78,7 +76,11 @@ describe('SentryBrowser', () => { describe('user', () => { const EX_USER = { email: 'test@example.com' }; const client = new BrowserClient({ dsn }); - spy(client, 'showReportDialog'); + const reportDialogSpy = jest.spyOn(client, 'showReportDialog'); + + beforeEach(() => { + reportDialogSpy.mockReset(); + }); it('uses the user on the scope', () => { configureScope(scope => { @@ -88,8 +90,8 @@ describe('SentryBrowser', () => { showReportDialog(); - expect((client.showReportDialog as SinonSpy).called).to.be.true; - expect((client.showReportDialog as SinonSpy).lastCall.args[0].user.email).to.eq(EX_USER.email); + expect(reportDialogSpy).toBeCalled(); + expect(reportDialogSpy.mock.calls[0][0]!.user!.email).toBe(EX_USER.email); }); it('prioritizes options user over scope user', () => { @@ -101,8 +103,8 @@ describe('SentryBrowser', () => { const DIALOG_OPTION_USER = { email: 'option@example.com' }; showReportDialog({ user: DIALOG_OPTION_USER }); - expect((client.showReportDialog as SinonSpy).called).to.be.true; - expect((client.showReportDialog as SinonSpy).lastCall.args[0].user.email).to.eq(DIALOG_OPTION_USER.email); + expect(reportDialogSpy).toBeCalled(); + expect(reportDialogSpy.mock.calls[0][0]!.user!.email).toBe(DIALOG_OPTION_USER.email); }); }); }); @@ -114,7 +116,7 @@ describe('SentryBrowser', () => { captureMessage('event'); await flush(2000); - expect(beforeSend.args[0][0].breadcrumbs).to.have.lengthOf(2); + expect(beforeSend.mock.calls[0][0].breadcrumbs).toHaveLength(2); }); }); @@ -128,22 +130,20 @@ describe('SentryBrowser', () => { await flush(2000); - const event = beforeSend.args[0][0]; - expect(event.exception).to.not.be.undefined; - /* eslint-disable @typescript-eslint/no-non-null-assertion */ - expect(event.exception!.values![0]).to.not.be.undefined; - expect(event.exception!.values![0].type).to.equal('Error'); - expect(event.exception!.values![0].value).to.equal('test'); - expect(event.exception!.values![0].stacktrace).to.not.be.empty; - /* eslint-enable @typescript-eslint/no-non-null-assertion */ + const event = beforeSend.mock.calls[0][0]; + expect(event.exception).toBeDefined(); + expect(event.exception.values[0]).toBeDefined(); + expect(event.exception.values[0].type).toBe('Error'); + expect(event.exception.values[0].value).toBe('test'); + expect(event.exception.values[0].stacktrace.frames).not.toHaveLength(0); }); it('should capture a message', done => { getCurrentHub().bindClient( new BrowserClient({ beforeSend: (event: Event): Event | null => { - expect(event.message).to.equal('test'); - expect(event.exception).to.be.undefined; + expect(event.message).toBe('test'); + expect(event.exception).toBeUndefined(); done(); return event; }, @@ -157,8 +157,8 @@ describe('SentryBrowser', () => { getCurrentHub().bindClient( new BrowserClient({ beforeSend: (event: Event): Event | null => { - expect(event.message).to.equal('event'); - expect(event.exception).to.be.undefined; + expect(event.message).toBe('event'); + expect(event.exception).toBeUndefined(); done(); return event; }, @@ -169,7 +169,7 @@ describe('SentryBrowser', () => { }); it('should not dedupe an event on bound client', async () => { - const localBeforeSend = spy(); + const localBeforeSend = jest.fn(); getCurrentHub().bindClient( new BrowserClient({ beforeSend: localBeforeSend, @@ -183,11 +183,11 @@ describe('SentryBrowser', () => { await flush(10); - expect(localBeforeSend.calledTwice).to.be.true; + expect(localBeforeSend).toHaveBeenCalledTimes(2); }); it('should use inboundfilter rules of bound client', async () => { - const localBeforeSend = spy(); + const localBeforeSend = jest.fn(); getCurrentHub().bindClient( new BrowserClient({ beforeSend: localBeforeSend, @@ -200,7 +200,7 @@ describe('SentryBrowser', () => { await flush(2000); - expect(localBeforeSend.called).to.be.false; + expect(localBeforeSend).not.toHaveBeenCalled(); }); }); }); @@ -209,20 +209,20 @@ describe('SentryBrowser initialization', () => { it('should use window.SENTRY_RELEASE to set release on initialization if available', () => { global.SENTRY_RELEASE = { id: 'foobar' }; init({ dsn }); - expect(global.__SENTRY__.hub._stack[0].client.getOptions().release).to.equal('foobar'); + expect(global.__SENTRY__.hub._stack[0].client.getOptions().release).toBe('foobar'); delete global.SENTRY_RELEASE; }); it('should use initialScope', () => { init({ dsn, initialScope: { tags: { a: 'b' } } }); - expect(global.__SENTRY__.hub._stack[0].scope._tags).to.deep.equal({ a: 'b' }); + expect(global.__SENTRY__.hub._stack[0].scope._tags).toEqual({ a: 'b' }); }); it('should use initialScope Scope', () => { const scope = new Scope(); scope.setTags({ a: 'b' }); init({ dsn, initialScope: scope }); - expect(global.__SENTRY__.hub._stack[0].scope._tags).to.deep.equal({ a: 'b' }); + expect(global.__SENTRY__.hub._stack[0].scope._tags).toEqual({ a: 'b' }); }); it('should use initialScope callback', () => { @@ -233,38 +233,36 @@ describe('SentryBrowser initialization', () => { return scope; }, }); - expect(global.__SENTRY__.hub._stack[0].scope._tags).to.deep.equal({ a: 'b' }); + expect(global.__SENTRY__.hub._stack[0].scope._tags).toEqual({ a: 'b' }); }); it('should have initialization proceed as normal if window.SENTRY_RELEASE is not set', () => { // This is mostly a happy-path test to ensure that the initialization doesn't throw an error. init({ dsn }); - expect(global.__SENTRY__.hub._stack[0].client.getOptions().release).to.be.undefined; + expect(global.__SENTRY__.hub._stack[0].client.getOptions().release).toBeUndefined(); }); describe('SDK metadata', () => { it('should set SDK data when Sentry.init() is called', () => { init({ dsn }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any const sdkData = (getCurrentHub().getClient() as any)._backend._transport._api.metadata?.sdk; - expect(sdkData.name).to.equal('sentry.javascript.browser'); - expect(sdkData.packages[0].name).to.equal('npm:@sentry/browser'); - expect(sdkData.packages[0].version).to.equal(SDK_VERSION); - expect(sdkData.version).to.equal(SDK_VERSION); + expect(sdkData.name).toBe('sentry.javascript.browser'); + expect(sdkData.packages[0].name).toBe('npm:@sentry/browser'); + expect(sdkData.packages[0].version).toBe(SDK_VERSION); + expect(sdkData.version).toBe(SDK_VERSION); }); it('should set SDK data when instantiating a client directly', () => { const client = new BrowserClient({ dsn }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any const sdkData = (client as any)._backend._transport._api.metadata?.sdk; - expect(sdkData.name).to.equal('sentry.javascript.browser'); - expect(sdkData.packages[0].name).to.equal('npm:@sentry/browser'); - expect(sdkData.packages[0].version).to.equal(SDK_VERSION); - expect(sdkData.version).to.equal(SDK_VERSION); + expect(sdkData.name).toBe('sentry.javascript.browser'); + expect(sdkData.packages[0].name).toBe('npm:@sentry/browser'); + expect(sdkData.packages[0].version).toBe(SDK_VERSION); + expect(sdkData.version).toBe(SDK_VERSION); }); // wrapper packages (like @sentry/angular and @sentry/react) set their SDK data in their `init` methods, which are @@ -287,13 +285,12 @@ describe('SentryBrowser initialization', () => { }, }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any const sdkData = (getCurrentHub().getClient() as any)._backend._transport._api.metadata?.sdk; - expect(sdkData.name).to.equal('sentry.javascript.angular'); - expect(sdkData.packages[0].name).to.equal('npm:@sentry/angular'); - expect(sdkData.packages[0].version).to.equal(SDK_VERSION); - expect(sdkData.version).to.equal(SDK_VERSION); + expect(sdkData.name).toBe('sentry.javascript.angular'); + expect(sdkData.packages[0].name).toBe('npm:@sentry/angular'); + expect(sdkData.packages[0].version).toBe(SDK_VERSION); + expect(sdkData.version).toBe(SDK_VERSION); }); }); }); @@ -303,10 +300,8 @@ describe('wrap()', () => { getCurrentHub().bindClient( new BrowserClient({ beforeSend: (event: Event): Event | null => { - /* eslint-disable @typescript-eslint/no-non-null-assertion */ - expect(event.exception!.values![0].type).to.equal('TypeError'); - expect(event.exception!.values![0].value).to.equal('mkey'); - /* eslint-enable @typescript-eslint/no-non-null-assertion */ + expect(event.exception!.values![0].type).toBe('TypeError'); + expect(event.exception!.values![0].value).toBe('mkey'); done(); return null; }, @@ -314,14 +309,18 @@ describe('wrap()', () => { }), ); - wrap(() => { - throw new TypeError('mkey'); - }); + try { + wrap(() => { + throw new TypeError('mkey'); + }); + } catch (e) { + // no-empty + } }); it('should return result of a function call', () => { const result = wrap(() => 2); - expect(result).to.equal(2); + expect(result).toBe(2); }); it('should allow for passing this and arguments through binding', () => { @@ -331,9 +330,9 @@ describe('wrap()', () => { }.bind({ context: 'this' }, 'b', 42), ); - expect((result as unknown[])[0]).to.deep.equal({ context: 'this' }); - expect((result as unknown[])[1]).to.equal('b'); - expect((result as unknown[])[2]).to.equal(42); + expect((result as unknown[])[0]).toEqual({ context: 'this' }); + expect((result as unknown[])[1]).toBe('b'); + expect((result as unknown[])[2]).toBe(42); const result2 = wrap( function(this: { x: number }): number { @@ -341,6 +340,6 @@ describe('wrap()', () => { }.bind({ x: 42 }), ); - expect(result2).to.equal(42); + expect(result2).toBe(42); }); }); diff --git a/packages/browser/test/unit/integrations/helpers.test.ts b/packages/browser/test/unit/integrations/helpers.test.ts index 7be42edd1ce1..e213493797bf 100644 --- a/packages/browser/test/unit/integrations/helpers.test.ts +++ b/packages/browser/test/unit/integrations/helpers.test.ts @@ -1,5 +1,4 @@ import { WrappedFunction } from '@sentry/types'; -import { expect } from 'chai'; import { SinonSpy, spy } from 'sinon'; import { wrap } from '../../../src/helpers'; @@ -12,22 +11,22 @@ describe('internal wrap()', () => { const str = 'Rick'; const num = 42; - expect(wrap(fn)).not.equal(fn); + expect(wrap(fn)).not.toBe(fn); // @ts-ignore Issue with `WrappedFunction` type from wrap fn - expect(wrap(obj)).equal(obj); + expect(wrap(obj)).toBe(obj); // @ts-ignore Issue with `WrappedFunction` type from wrap fn - expect(wrap(arr)).equal(arr); + expect(wrap(arr)).toBe(arr); // @ts-ignore Issue with `WrappedFunction` type from wrap fn - expect(wrap(str)).equal(str); + expect(wrap(str)).toBe(str); // @ts-ignore Issue with `WrappedFunction` type from wrap fn - expect(wrap(num)).equal(num); + expect(wrap(num)).toBe(num); }); it('should preserve correct function name when accessed', () => { const namedFunction = (): number => 1337; - expect(wrap(namedFunction)).not.equal(namedFunction); - expect(namedFunction.name).equal('namedFunction'); - expect(wrap(namedFunction).name).equal('namedFunction'); + expect(wrap(namedFunction)).not.toBe(namedFunction); + expect(namedFunction.name).toBe('namedFunction'); + expect(wrap(namedFunction).name).toBe('namedFunction'); }); it('bail out with the original if accessing custom props go bad', () => { @@ -39,7 +38,7 @@ describe('internal wrap()', () => { }, }); - expect(wrap(fn)).equal(fn); + expect(wrap(fn)).toBe(fn); Object.defineProperty(fn, '__sentry__', { get(): void { @@ -48,14 +47,14 @@ describe('internal wrap()', () => { configurable: true, }); - expect(wrap(fn)).equal(fn); + expect(wrap(fn)).toBe(fn); }); it('returns wrapped function if original was already wrapped', () => { const fn = (() => 1337) as WrappedFunction; const wrapped = wrap(fn); - expect(wrap(fn)).equal(wrapped); + expect(wrap(fn)).toBe(wrapped); }); it('returns same wrapped function if trying to wrap it again', () => { @@ -63,7 +62,7 @@ describe('internal wrap()', () => { const wrapped = wrap(fn); - expect(wrap(wrapped)).equal(wrapped); + expect(wrap(wrapped)).toBe(wrapped); }); it('calls "before" function when invoking wrapped function', () => { @@ -73,7 +72,7 @@ describe('internal wrap()', () => { const wrapped = wrap(fn, {}, before); wrapped(); - expect(before.called).equal(true); + expect(before.called).toBe(true); }); it('attaches metadata to original and wrapped functions', () => { @@ -81,14 +80,14 @@ describe('internal wrap()', () => { const wrapped = wrap(fn); - expect(fn).to.have.property('__sentry_wrapped__'); - expect(fn.__sentry_wrapped__).equal(wrapped); + expect(fn).toHaveProperty('__sentry_wrapped__'); + expect(fn.__sentry_wrapped__).toBe(wrapped); - expect(wrapped).to.have.property('__sentry__'); - expect(wrapped.__sentry__).equal(true); + expect(wrapped).toHaveProperty('__sentry__'); + expect(wrapped.__sentry__).toBe(true); - expect(wrapped).to.have.property('__sentry_original__'); - expect(wrapped.__sentry_original__).equal(fn); + expect(wrapped).toHaveProperty('__sentry_original__'); + expect(wrapped.__sentry_original__).toBe(fn); }); it('copies over original functions properties', () => { @@ -98,10 +97,10 @@ describe('internal wrap()', () => { const wrapped = wrap(fn); - expect(wrapped).to.have.property('some'); - expect(wrapped.some).equal(1337); - expect(wrapped).to.have.property('property'); - expect(wrapped.property).equal('Rick'); + expect(wrapped).toHaveProperty('some'); + expect(wrapped.some).toBe(1337); + expect(wrapped).toHaveProperty('property'); + expect(wrapped.property).toBe('Rick'); }); it('doesnt break when accessing original functions properties blows up', () => { @@ -114,7 +113,7 @@ describe('internal wrap()', () => { const wrapped = wrap(fn); - expect(wrapped).to.not.have.property('some'); + expect(wrapped).not.toHaveProperty('some'); }); it('recrusively wraps arguments that are functions', () => { @@ -125,8 +124,8 @@ describe('internal wrap()', () => { const wrapped = wrap(fn); wrapped(fnArgA, fnArgB); - expect(fnArgA).to.have.property('__sentry_wrapped__'); - expect(fnArgB).to.have.property('__sentry_wrapped__'); + expect(fnArgA).toHaveProperty('__sentry_wrapped__'); + expect(fnArgB).toHaveProperty('__sentry_wrapped__'); }); it('calls either `handleEvent` property if it exists or the original function', () => { @@ -141,21 +140,21 @@ describe('internal wrap()', () => { wrap(fn)(123, 'Rick'); wrap(eventFn)(123, 'Morty'); - expect(fn.called).equal(true); - expect(fn.getCalls()[0].args[0]).equal(123); - expect(fn.getCalls()[0].args[1]).equal('Rick'); + expect(fn.called).toBe(true); + expect(fn.getCalls()[0].args[0]).toBe(123); + expect(fn.getCalls()[0].args[1]).toBe('Rick'); - expect(eventFn.handleEvent.called).equal(true); - expect(eventFn.handleEvent.getCalls()[0].args[0]).equal(123); - expect(eventFn.handleEvent.getCalls()[0].args[1]).equal('Morty'); + expect(eventFn.handleEvent.called).toBe(true); + expect(eventFn.handleEvent.getCalls()[0].args[0]).toBe(123); + expect(eventFn.handleEvent.getCalls()[0].args[1]).toBe('Morty'); - expect(eventFn.called).equal(false); + expect(eventFn.called).toBe(false); }); it('preserves `this` context for all the calls', () => { const context = { fn(): void { - expect(this).equal(context); + expect(this).toBe(context); }, eventFn(): void { return; @@ -163,7 +162,7 @@ describe('internal wrap()', () => { }; // @ts-ignore eventFn does not have property handleEvent context.eventFn.handleEvent = function(): void { - expect(this).equal(context); + expect(this).toBe(context); }; // eslint-disable-next-line @typescript-eslint/unbound-method @@ -184,7 +183,7 @@ describe('internal wrap()', () => { try { wrapped(); } catch (error) { - expect(error.message).equal('boom'); + expect(error.message).toBe('boom'); } }); @@ -193,15 +192,15 @@ describe('internal wrap()', () => { const wrapped = wrap(fn); // Shouldn't show up in iteration - expect(Object.keys(fn)).to.not.include('__sentry__'); - expect(Object.keys(fn)).to.not.include('__sentry_original__'); - expect(Object.keys(fn)).to.not.include('__sentry_wrapped__'); - expect(Object.keys(wrapped)).to.not.include('__sentry__'); - expect(Object.keys(wrapped)).to.not.include('__sentry_original__'); - expect(Object.keys(wrapped)).to.not.include('__sentry_wrapped__'); + expect(Object.keys(fn)).toEqual(expect.not.arrayContaining(['__sentry__'])); + expect(Object.keys(fn)).toEqual(expect.not.arrayContaining(['__sentry_original__'])); + expect(Object.keys(fn)).toEqual(expect.not.arrayContaining(['__sentry_wrapped__'])); + expect(Object.keys(wrapped)).toEqual(expect.not.arrayContaining(['__sentry__'])); + expect(Object.keys(wrapped)).toEqual(expect.not.arrayContaining(['__sentry_original__'])); + expect(Object.keys(wrapped)).toEqual(expect.not.arrayContaining(['__sentry_wrapped__'])); // But should be accessible directly - expect(wrapped.__sentry__).to.equal(true); - expect(wrapped.__sentry_original__).to.equal(fn); - expect(fn.__sentry_wrapped__).to.equal(wrapped); + expect(wrapped.__sentry__).toBe(true); + expect(wrapped.__sentry_original__).toBe(fn); + expect(fn.__sentry_wrapped__).toBe(wrapped); }); }); diff --git a/packages/browser/test/unit/integrations/linkederrors.test.ts b/packages/browser/test/unit/integrations/linkederrors.test.ts index fc98788803f3..15a448a47570 100644 --- a/packages/browser/test/unit/integrations/linkederrors.test.ts +++ b/packages/browser/test/unit/integrations/linkederrors.test.ts @@ -1,11 +1,9 @@ import { ExtendedError } from '@sentry/types'; -import { expect } from 'chai'; import { stub } from 'sinon'; import { BrowserBackend } from '../../../src/backend'; import { LinkedErrors } from '../../../src/integrations/linkederrors'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any let linkedErrors: any; describe('LinkedErrors', () => { @@ -20,8 +18,8 @@ describe('LinkedErrors', () => { message: 'foo', }; const result = linkedErrors._handler(event); - expect(spy.called).equal(false); - expect(result).to.deep.equal(event); + expect(spy.called).toBe(false); + expect(result).toEqual(event); }); it('should bail out if event contains exception, but no hint', () => { @@ -33,8 +31,8 @@ describe('LinkedErrors', () => { message: 'foo', }; const result = linkedErrors._handler(event); - expect(spy.called).equal(false); - expect(result).to.deep.equal(event); + expect(spy.called).toBe(false); + expect(result).toEqual(event); }); it('should call walkErrorTree if event contains exception and hint with originalException', () => { @@ -49,7 +47,7 @@ describe('LinkedErrors', () => { originalException: new Error('originalException'), }; linkedErrors._handler(event, hint); - expect(spy.calledOnce).equal(true); + expect(spy.calledOnce).toBe(true); }); it('should recursively walk error to find linked exceptions and assign them to the event', async () => { @@ -69,16 +67,16 @@ describe('LinkedErrors', () => { }); // It shouldn't include root exception, as it's already processed in the event by the main error handler - expect(result.exception.values.length).equal(3); - expect(result.exception.values[0].type).equal('SyntaxError'); - expect(result.exception.values[0].value).equal('three'); - expect(result.exception.values[0].stacktrace).to.have.property('frames'); - expect(result.exception.values[1].type).equal('TypeError'); - expect(result.exception.values[1].value).equal('two'); - expect(result.exception.values[1].stacktrace).to.have.property('frames'); - expect(result.exception.values[2].type).equal('Error'); - expect(result.exception.values[2].value).equal('one'); - expect(result.exception.values[2].stacktrace).to.have.property('frames'); + expect(result.exception.values.length).toBe(3); + expect(result.exception.values[0].type).toBe('SyntaxError'); + expect(result.exception.values[0].value).toBe('three'); + expect(result.exception.values[0].stacktrace).toHaveProperty('frames'); + expect(result.exception.values[1].type).toBe('TypeError'); + expect(result.exception.values[1].value).toBe('two'); + expect(result.exception.values[1].stacktrace).toHaveProperty('frames'); + expect(result.exception.values[2].type).toBe('Error'); + expect(result.exception.values[2].value).toBe('one'); + expect(result.exception.values[2].stacktrace).toHaveProperty('frames'); }); }); @@ -102,16 +100,16 @@ describe('LinkedErrors', () => { originalException, }); - expect(result.exception.values.length).equal(3); - expect(result.exception.values[0].type).equal('SyntaxError'); - expect(result.exception.values[0].value).equal('three'); - expect(result.exception.values[0].stacktrace).to.have.property('frames'); - expect(result.exception.values[1].type).equal('TypeError'); - expect(result.exception.values[1].value).equal('two'); - expect(result.exception.values[1].stacktrace).to.have.property('frames'); - expect(result.exception.values[2].type).equal('Error'); - expect(result.exception.values[2].value).equal('one'); - expect(result.exception.values[2].stacktrace).to.have.property('frames'); + expect(result.exception.values.length).toBe(3); + expect(result.exception.values[0].type).toBe('SyntaxError'); + expect(result.exception.values[0].value).toBe('three'); + expect(result.exception.values[0].stacktrace).toHaveProperty('frames'); + expect(result.exception.values[1].type).toBe('TypeError'); + expect(result.exception.values[1].value).toBe('two'); + expect(result.exception.values[1].stacktrace).toHaveProperty('frames'); + expect(result.exception.values[2].type).toBe('Error'); + expect(result.exception.values[2].value).toBe('one'); + expect(result.exception.values[2].stacktrace).toHaveProperty('frames'); }); }); @@ -132,13 +130,13 @@ describe('LinkedErrors', () => { originalException: one, }); - expect(result.exception.values.length).equal(2); - expect(result.exception.values[0].type).equal('TypeError'); - expect(result.exception.values[0].value).equal('two'); - expect(result.exception.values[0].stacktrace).to.have.property('frames'); - expect(result.exception.values[1].type).equal('Error'); - expect(result.exception.values[1].value).equal('one'); - expect(result.exception.values[1].stacktrace).to.have.property('frames'); + expect(result.exception.values.length).toBe(2); + expect(result.exception.values[0].type).toBe('TypeError'); + expect(result.exception.values[0].value).toBe('two'); + expect(result.exception.values[0].stacktrace).toHaveProperty('frames'); + expect(result.exception.values[1].type).toBe('Error'); + expect(result.exception.values[1].value).toBe('one'); + expect(result.exception.values[1].stacktrace).toHaveProperty('frames'); }); }); }); diff --git a/packages/browser/test/unit/jest.config.js b/packages/browser/test/unit/jest.config.js new file mode 100644 index 000000000000..4c507d7f5440 --- /dev/null +++ b/packages/browser/test/unit/jest.config.js @@ -0,0 +1,14 @@ +module.exports = { + transform: { + '^.+\\.ts$': 'ts-jest', + }, + moduleFileExtensions: ['js', 'ts'], + testEnvironment: 'jsdom', + testMatch: ['**/*.test.ts'], + globals: { + 'ts-jest': { + tsConfig: '../../tsconfig.json', + diagnostics: false, + }, + }, +}; diff --git a/packages/browser/test/unit/karma.conf.js b/packages/browser/test/unit/karma.conf.js deleted file mode 100644 index 50223c63a8cc..000000000000 --- a/packages/browser/test/unit/karma.conf.js +++ /dev/null @@ -1,43 +0,0 @@ -module.exports = config => { - config.set({ - colors: true, - singleRun: true, - autoWatch: false, - basePath: process.cwd(), - files: ['test/unit/**/*.ts', 'src/**/*.+(js|ts)'], - frameworks: ['mocha', 'chai', 'sinon', 'karma-typescript'], - browsers: ['ChromeHeadless'], - reporters: ['mocha', 'karma-typescript'], - preprocessors: { - '**/*.+(js|ts)': ['karma-typescript'], - }, - karmaTypescriptConfig: { - tsconfig: 'tsconfig.json', - compilerOptions: { - allowJs: true, - declaration: false, - declarationMap: false, - paths: { - '@sentry/utils/*': ['../../../utils/src/*'], - '@sentry/core': ['../../../core/src'], - '@sentry/hub': ['../../../hub/src'], - '@sentry/types': ['../../../types/src'], - '@sentry/minimal': ['../../../minimal/src'], - }, - }, - bundlerOptions: { - sourceMap: true, - transforms: [require('karma-typescript-es6-transform')()], - }, - include: ['test/unit/**/*.ts'], - reports: { - html: 'coverage', - 'text-summary': '', - }, - }, - // Uncomment if you want to silence console logs in the output - // client: { - // captureConsole: false, - // }, - }); -}; diff --git a/packages/browser/test/unit/parsers.test.ts b/packages/browser/test/unit/parsers.test.ts index a8c4ae63e327..675ac19e62e7 100644 --- a/packages/browser/test/unit/parsers.test.ts +++ b/packages/browser/test/unit/parsers.test.ts @@ -1,10 +1,8 @@ -import { expect } from 'chai'; - import { prepareFramesForEvent } from '../../src/parsers'; describe('Parsers', () => { describe('prepareFramesForEvent()', () => { - describe('removed top frame if its internally reserved word (public API)', async () => { + describe('removed top frame if its internally reserved word (public API)', () => { it('reserved captureException', () => { const stack = [ { context: ['x'], column: 1, line: 4, url: 'anything.js', func: 'captureException', args: [] }, @@ -15,9 +13,9 @@ describe('Parsers', () => { // Should remove `captureException` as its a name considered "internal" const frames = prepareFramesForEvent(stack); - expect(frames.length).equal(2); - expect(frames[0].function).equal('bar'); - expect(frames[1].function).equal('foo'); + expect(frames.length).toBe(2); + expect(frames[0].function).toBe('bar'); + expect(frames[1].function).toBe('foo'); }); it('reserved captureMessage', () => { @@ -30,13 +28,13 @@ describe('Parsers', () => { // Should remove `captureMessage` as its a name considered "internal" const frames = prepareFramesForEvent(stack); - expect(frames.length).equal(2); - expect(frames[0].function).equal('bar'); - expect(frames[1].function).equal('foo'); + expect(frames.length).toBe(2); + expect(frames[0].function).toBe('bar'); + expect(frames[1].function).toBe('foo'); }); }); - describe('removed bottom frame if its internally reserved word (internal API)', async () => { + describe('removed bottom frame if its internally reserved word (internal API)', () => { it('reserved sentryWrapped', () => { const stack = [ { context: ['x'], column: 1, line: 3, url: 'anything.js', func: 'foo', args: [] }, @@ -47,13 +45,13 @@ describe('Parsers', () => { // Should remove `sentryWrapped` as its a name considered "internal" const frames = prepareFramesForEvent(stack); - expect(frames.length).equal(2); - expect(frames[0].function).equal('bar'); - expect(frames[1].function).equal('foo'); + expect(frames.length).toBe(2); + expect(frames[0].function).toBe('bar'); + expect(frames[1].function).toBe('foo'); }); }); - it('removed top and bottom frame if they are internally reserved words', async () => { + it('removed top and bottom frame if they are internally reserved words', () => { const stack = [ { context: ['x'], column: 1, line: 4, url: 'anything.js', func: 'captureMessage', args: [] }, { context: ['x'], column: 1, line: 3, url: 'anything.js', func: 'foo', args: [] }, @@ -64,9 +62,9 @@ describe('Parsers', () => { // Should remove `captureMessage` and `sentryWrapped` as its a name considered "internal" const frames = prepareFramesForEvent(stack); - expect(frames.length).equal(2); - expect(frames[0].function).equal('bar'); - expect(frames[1].function).equal('foo'); + expect(frames.length).toBe(2); + expect(frames[0].function).toBe('bar'); + expect(frames[1].function).toBe('foo'); }); }); }); diff --git a/packages/browser/test/unit/tracekit/custom.test.ts b/packages/browser/test/unit/tracekit/custom.test.ts index 2fbaabf9edfb..a9350d6d6006 100644 --- a/packages/browser/test/unit/tracekit/custom.test.ts +++ b/packages/browser/test/unit/tracekit/custom.test.ts @@ -1,5 +1,3 @@ -import { expect } from 'chai'; - import { computeStackTrace } from '../../../src/tracekit'; describe('Tracekit - Custom Tests', () => { @@ -13,7 +11,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(CHROMIUM_EMBEDDED_FRAMEWORK_CUSTOM_SCHEME); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { args: [], column: 15, @@ -34,7 +32,7 @@ describe('Tracekit - Custom Tests', () => { at safari-extension:(//3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/topee-content.js:3313:26)`, }; const stacktrace = computeStackTrace(SAFARI_EXTENSION_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { url: 'safari-extension://3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/commons.js', func: 'ClipperError', @@ -62,7 +60,7 @@ describe('Tracekit - Custom Tests', () => { }; const stacktrace = computeStackTrace(SAFARI_EXTENSION_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { url: 'safari-extension://com.grammarly.safari.extension.ext2-W8F64X92K3/ee7759dd/Grammarly.js', func: 'isClaimed', @@ -96,7 +94,7 @@ describe('Tracekit - Custom Tests', () => { at safari-web-extension:(//3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/topee-content.js:3313:26)`, }; const stacktrace = computeStackTrace(SAFARI_WEB_EXTENSION_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { url: 'safari-web-extension://3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/commons.js', func: 'ClipperError', @@ -124,7 +122,7 @@ describe('Tracekit - Custom Tests', () => { }; const stacktrace = computeStackTrace(SAFARI_EXTENSION_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { url: 'safari-web-extension://46434E60-F5BD-48A4-80C8-A422C5D16897/scripts/content-script.js', func: 'p_', @@ -164,7 +162,7 @@ describe('Tracekit - Custom Tests', () => { at P(index.android.bundle:93:714)`, }; const stacktrace = computeStackTrace(REACT_NATIVE_V8_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { url: 'index.android.bundle', func: 'Object.onPress', args: [], line: 2342, column: 3773 }, { url: 'index.android.bundle', func: 's.touchableHandlePress', args: [], line: 214, column: 2048 }, { url: 'index.android.bundle', func: 's._performSideEffectsForTransition', args: [], line: 198, column: 9608 }, @@ -186,7 +184,7 @@ describe('Tracekit - Custom Tests', () => { forEach@[native code]`, }; const stacktrace = computeStackTrace(REACT_NATIVE_EXPO_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { url: '/data/user/0/com.sentrytest/files/.expo-internal/bundle-613EDD44F3305B9D75D4679663900F2BCDDDC326F247CA3202A3A4219FD412D3', @@ -243,7 +241,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(CHROME73_NATIVE_CODE_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { args: [], column: 17, func: 'fooIterator', line: 20, url: 'http://localhost:5000/test' }, { args: [], column: null, func: 'Array.map', line: null, url: '' }, { args: [], column: 19, func: 'foo', line: 19, url: 'http://localhost:5000/test' }, @@ -262,7 +260,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(FIREFOX66_NATIVE_CODE_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { args: [], column: 17, func: 'fooIterator', line: 20, url: 'http://localhost:5000/test' }, { args: [], column: 19, func: 'foo', line: 19, url: 'http://localhost:5000/test' }, { args: [], column: 7, func: '?', line: 24, url: 'http://localhost:5000/test' }, @@ -281,7 +279,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(SAFARI12_NATIVE_CODE_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { args: [], column: 26, func: 'fooIterator', line: 20, url: 'http://localhost:5000/test' }, { args: [], column: null, func: 'map', line: null, url: '[native code]' }, { args: [], column: 22, func: 'foo', line: 19, url: 'http://localhost:5000/test' }, @@ -302,7 +300,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(EDGE44_NATIVE_CODE_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { args: [], column: 11, func: 'fooIterator', line: 20, url: 'http://localhost:5000/test' }, { args: ['native code'], @@ -337,7 +335,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(CHROME73_EVAL_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { column: 13, url: 'http://localhost:5000/', func: 'Object.aha', line: 19, args: [] }, { column: 16, url: 'http://localhost:5000/', func: 'callAnotherThing', line: 20, args: [] }, { column: 7, url: 'http://localhost:5000/', func: 'Object.callback', line: 25, args: [] }, @@ -368,7 +366,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(FIREFOX66_EVAL_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { column: 13, url: 'http://localhost:5000/', func: 'aha', line: 19, args: [] }, { column: 15, url: 'http://localhost:5000/', func: 'callAnotherThing', line: 20, args: [] }, { column: 7, url: 'http://localhost:5000/', func: 'callback', line: 25, args: [] }, @@ -401,7 +399,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(SAFARI12_EVAL_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { column: 22, url: 'http://localhost:5000/', func: 'aha', line: 19, args: [] }, { column: null, url: '[native code]', func: 'aha', line: null, args: [] }, { column: 16, url: 'http://localhost:5000/', func: 'callAnotherThing', line: 20, args: [] }, @@ -435,7 +433,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(EDGE44_EVAL_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { column: 7, url: 'http://localhost:5000/', func: 'aha', line: 19, args: [] }, { column: 6, url: 'http://localhost:5000/', func: 'callAnotherThing', line: 18, args: [] }, { column: 7, url: 'http://localhost:5000/', func: 'callback', line: 25, args: [] }, @@ -473,7 +471,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(CHROME_ELECTRON_RENDERER); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { args: [], column: 108, @@ -501,7 +499,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(REACT_INVARIANT_VIOLATION_EXCEPTION); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { args: [], column: 21738, @@ -547,7 +545,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(REACT_PRODUCTION_ERROR); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { args: [], column: 21738, @@ -594,7 +592,7 @@ describe('Tracekit - Custom Tests', () => { const stacktrace = computeStackTrace(REACT_PRODUCTION_ERROR); - expect(stacktrace.stack).deep.equal([ + expect(stacktrace.stack).toEqual([ { args: [], column: 21738, diff --git a/packages/browser/test/unit/tracekit/original.test.ts b/packages/browser/test/unit/tracekit/original.test.ts index af0de34c91d7..1fb3e43e7585 100644 --- a/packages/browser/test/unit/tracekit/original.test.ts +++ b/packages/browser/test/unit/tracekit/original.test.ts @@ -1,5 +1,3 @@ -import { expect } from 'chai'; - import { computeStackTrace } from '../../../src/tracekit'; import { ANDROID_REACT_NATIVE, @@ -34,30 +32,30 @@ import { describe('Tracekit - Original Tests', () => { it('should parse Safari 6 error', () => { const stackFrames = computeStackTrace(SAFARI_6); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(4); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(4); + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: '?', args: [], line: 48, column: null, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'dumpException3', args: [], line: 52, column: null, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'onclick', args: [], line: 82, column: null, }); - expect(stackFrames.stack[3]).to.deep.equal({ + expect(stackFrames.stack[3]).toEqual({ url: '[native code]', func: '?', args: [], @@ -68,23 +66,23 @@ describe('Tracekit - Original Tests', () => { it('should parse Safari 7 error', () => { const stackFrames = computeStackTrace(SAFARI_7); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: '?', args: [], line: 48, column: 22, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 52, column: 15, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], @@ -95,23 +93,23 @@ describe('Tracekit - Original Tests', () => { it('should parse Safari 8 error', () => { const stackFrames = computeStackTrace(SAFARI_8); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: '?', args: [], line: 47, column: 22, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 52, column: 15, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], @@ -123,23 +121,23 @@ describe('Tracekit - Original Tests', () => { it('should parse Safari 8 eval error', () => { // TODO: Take into account the line and column properties on the error object and use them for the first stack trace. const stackFrames = computeStackTrace(SAFARI_8_EVAL); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ url: '[native code]', func: 'eval', args: [], line: null, column: null, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 58, column: 21, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], @@ -150,51 +148,51 @@ describe('Tracekit - Original Tests', () => { it('should parse Firefox 3 error', () => { const stackFrames = computeStackTrace(FIREFOX_3); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(7); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[0]).toEqual({ url: 'http://127.0.0.1:8000/js/stacktrace.js', func: '?', args: [], line: 44, column: null, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://127.0.0.1:8000/js/stacktrace.js', func: '?', args: ['null'], line: 31, column: null, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://127.0.0.1:8000/js/stacktrace.js', func: 'printStackTrace', args: [], line: 18, column: null, }); - expect(stackFrames.stack[3]).to.deep.equal({ + expect(stackFrames.stack[3]).toEqual({ url: 'http://127.0.0.1:8000/js/file.js', func: 'bar', args: ['1'], line: 13, column: null, }); - expect(stackFrames.stack[4]).to.deep.equal({ + expect(stackFrames.stack[4]).toEqual({ url: 'http://127.0.0.1:8000/js/file.js', func: 'bar', args: ['2'], line: 16, column: null, }); - expect(stackFrames.stack[5]).to.deep.equal({ + expect(stackFrames.stack[5]).toEqual({ url: 'http://127.0.0.1:8000/js/file.js', func: 'foo', args: [], line: 20, column: null, }); - expect(stackFrames.stack[6]).to.deep.equal({ + expect(stackFrames.stack[6]).toEqual({ url: 'http://127.0.0.1:8000/js/file.js', func: '?', args: [], @@ -205,51 +203,51 @@ describe('Tracekit - Original Tests', () => { it('should parse Firefox 7 error', () => { const stackFrames = computeStackTrace(FIREFOX_7); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(7); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[0]).toEqual({ url: 'file:///G:/js/stacktrace.js', func: '?', args: [], line: 44, column: null, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'file:///G:/js/stacktrace.js', func: '?', args: ['null'], line: 31, column: null, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'file:///G:/js/stacktrace.js', func: 'printStackTrace', args: [], line: 18, column: null, }); - expect(stackFrames.stack[3]).to.deep.equal({ + expect(stackFrames.stack[3]).toEqual({ url: 'file:///G:/js/file.js', func: 'bar', args: ['1'], line: 13, column: null, }); - expect(stackFrames.stack[4]).to.deep.equal({ + expect(stackFrames.stack[4]).toEqual({ url: 'file:///G:/js/file.js', func: 'bar', args: ['2'], line: 16, column: null, }); - expect(stackFrames.stack[5]).to.deep.equal({ + expect(stackFrames.stack[5]).toEqual({ url: 'file:///G:/js/file.js', func: 'foo', args: [], line: 20, column: null, }); - expect(stackFrames.stack[6]).to.deep.equal({ + expect(stackFrames.stack[6]).toEqual({ url: 'file:///G:/js/file.js', func: '?', args: [], @@ -260,23 +258,23 @@ describe('Tracekit - Original Tests', () => { it('should parse Firefox 14 error', () => { const stackFrames = computeStackTrace(FIREFOX_14); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: '?', args: [], line: 48, column: null, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'dumpException3', args: [], line: 52, column: null, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'onclick', args: [], @@ -287,23 +285,23 @@ describe('Tracekit - Original Tests', () => { it('should parse Firefox 31 error', () => { const stackFrames = computeStackTrace(FIREFOX_31); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 41, column: 13, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], line: 1, column: 1, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: '.plugin/e.fn[c]/<', args: [], @@ -314,30 +312,30 @@ describe('Tracekit - Original Tests', () => { it('should parse Firefox 44 ns exceptions', () => { const stackFrames = computeStackTrace(FIREFOX_44_NS_EXCEPTION); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(4); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(4); + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: '[2] { it('should parse Chrome error with no location', () => { const stackFrames = computeStackTrace({ message: 'foo', name: 'bar', stack: 'error\n at Array.forEach (native)' }); - expect(stackFrames.stack.length).to.be.equal(1); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames.stack.length).toBe(1); + expect(stackFrames.stack[0]).toEqual({ url: 'native', func: 'Array.forEach', args: ['native'], @@ -360,30 +358,30 @@ describe('Tracekit - Original Tests', () => { it('should parse Chrome 15 error', () => { const stackFrames = computeStackTrace(CHROME_15); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(4); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(4); + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], line: 13, column: 17, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], line: 16, column: 5, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 20, column: 5, }); - expect(stackFrames.stack[3]).to.deep.equal({ + expect(stackFrames.stack[3]).toEqual({ url: 'http://path/to/file.js', func: '?', args: [], @@ -394,23 +392,23 @@ describe('Tracekit - Original Tests', () => { it('should parse Chrome 36 error with port numbers', () => { const stackFrames = computeStackTrace(CHROME_36); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ url: 'http://localhost:8080/file.js', func: 'dumpExceptionError', args: [], line: 41, column: 27, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://localhost:8080/file.js', func: 'HTMLButtonElement.onclick', args: [], line: 107, column: 146, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://localhost:8080/file.js', func: 'I.e.fn.(anonymous function) [as index]', args: [], @@ -421,30 +419,30 @@ describe('Tracekit - Original Tests', () => { it('should parse Chrome error with webpack URLs', () => { const stackFrames = computeStackTrace(CHROME_XX_WEBPACK); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(4); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(4); + expect(stackFrames.stack[0]).toEqual({ url: 'webpack:///./src/components/test/test.jsx?', func: 'TESTTESTTEST.eval', args: [], line: 295, column: 108, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'webpack:///./src/components/test/test.jsx?', func: 'TESTTESTTEST.render', args: [], line: 272, column: 32, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'webpack:///./~/react-transform-catch-errors/lib/index.js?', func: 'TESTTESTTEST.tryRender', args: [], line: 34, column: 31, }); - expect(stackFrames.stack[3]).to.deep.equal({ + expect(stackFrames.stack[3]).toEqual({ url: 'webpack:///./~/react-proxy/modules/createPrototypeProxy.js?', func: 'TESTTESTTEST.proxiedMethod', args: [], @@ -455,37 +453,37 @@ describe('Tracekit - Original Tests', () => { it('should parse nested eval() from Chrome', () => { const stackFrames = computeStackTrace(CHROME_48_EVAL); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(5); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(5); + expect(stackFrames.stack[0]).toEqual({ url: 'http://localhost:8080/file.js', func: 'baz', args: [], line: 21, column: 17, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://localhost:8080/file.js', func: 'foo', args: [], line: 21, column: 17, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://localhost:8080/file.js', func: 'eval', args: [], line: 21, column: 17, }); - expect(stackFrames.stack[3]).to.deep.equal({ + expect(stackFrames.stack[3]).toEqual({ url: 'http://localhost:8080/file.js', func: 'Object.speak', args: [], line: 21, column: 17, }); - expect(stackFrames.stack[4]).to.deep.equal({ + expect(stackFrames.stack[4]).toEqual({ url: 'http://localhost:8080/file.js', func: '?', args: [], @@ -496,44 +494,44 @@ describe('Tracekit - Original Tests', () => { it('should parse Chrome error with blob URLs', () => { const stackFrames = computeStackTrace(CHROME_48_BLOB); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(7); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[1]).toEqual({ url: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', func: 's', args: [], line: 31, column: 29146, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', func: 'Object.d [as add]', args: [], line: 31, column: 30039, }); - expect(stackFrames.stack[3]).to.deep.equal({ + expect(stackFrames.stack[3]).toEqual({ url: 'blob:http%3A//localhost%3A8080/d4eefe0f-361a-4682-b217-76587d9f712a', func: '?', args: [], line: 15, column: 10978, }); - expect(stackFrames.stack[4]).to.deep.equal({ + expect(stackFrames.stack[4]).toEqual({ url: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', func: '?', args: [], line: 1, column: 6911, }); - expect(stackFrames.stack[5]).to.deep.equal({ + expect(stackFrames.stack[5]).toEqual({ url: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', func: 'n.fire', args: [], line: 7, column: 3019, }); - expect(stackFrames.stack[6]).to.deep.equal({ + expect(stackFrames.stack[6]).toEqual({ url: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', func: 'n.handle', args: [], @@ -544,24 +542,24 @@ describe('Tracekit - Original Tests', () => { it('should parse IE 10 error', () => { const stackFrames = computeStackTrace(IE_10); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); // TODO: func should be normalized - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: 'Anonymous function', args: [], line: 48, column: 13, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 46, column: 9, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], @@ -572,24 +570,24 @@ describe('Tracekit - Original Tests', () => { it('should parse IE 11 error', () => { const stackFrames = computeStackTrace(IE_11); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); // TODO: func should be normalized - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: 'Anonymous function', args: [], line: 47, column: 21, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 45, column: 13, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], @@ -600,23 +598,23 @@ describe('Tracekit - Original Tests', () => { it('should parse IE 11 eval error', () => { const stackFrames = computeStackTrace(IE_11_EVAL); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ url: 'eval code', func: 'eval code', args: [], line: 1, column: 1, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 58, column: 17, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], @@ -627,51 +625,51 @@ describe('Tracekit - Original Tests', () => { it('should parse Opera 10 error', () => { const stackFrames = computeStackTrace(OPERA_10); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(7); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: '?', args: [], line: 42, column: null, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: '?', args: [], line: 27, column: null, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'printStackTrace', args: [], line: 18, column: null, }); - expect(stackFrames.stack[3]).to.deep.equal({ + expect(stackFrames.stack[3]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], line: 4, column: null, }); - expect(stackFrames.stack[4]).to.deep.equal({ + expect(stackFrames.stack[4]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], line: 7, column: null, }); - expect(stackFrames.stack[5]).to.deep.equal({ + expect(stackFrames.stack[5]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 11, column: null, }); - expect(stackFrames.stack[6]).to.deep.equal({ + expect(stackFrames.stack[6]).toEqual({ url: 'http://path/to/file.js', func: '?', args: [], @@ -682,51 +680,51 @@ describe('Tracekit - Original Tests', () => { it('should parse Opera 11 error', () => { const stackFrames = computeStackTrace(OPERA_11); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(7); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: 'createException', args: [], line: 42, column: 12, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'run', args: ['ex'], line: 27, column: 8, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'printStackTrace', args: ['options'], line: 18, column: 4, }); - expect(stackFrames.stack[3]).to.deep.equal({ + expect(stackFrames.stack[3]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: ['n'], line: 4, column: 5, }); - expect(stackFrames.stack[4]).to.deep.equal({ + expect(stackFrames.stack[4]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: ['n'], line: 7, column: 4, }); - expect(stackFrames.stack[5]).to.deep.equal({ + expect(stackFrames.stack[5]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 11, column: 4, }); - expect(stackFrames.stack[6]).to.deep.equal({ + expect(stackFrames.stack[6]).toEqual({ url: 'http://path/to/file.js', func: '?', args: [], @@ -738,23 +736,23 @@ describe('Tracekit - Original Tests', () => { it('should parse Opera 12 error', () => { // TODO: Improve anonymous function name. const stackFrames = computeStackTrace(OPERA_12); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ url: 'http://localhost:8000/ExceptionLab.html', func: '', args: ['x'], line: 48, column: 12, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://localhost:8000/ExceptionLab.html', func: 'dumpException3', args: [], line: 46, column: 8, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://localhost:8000/ExceptionLab.html', func: '', args: ['event'], @@ -765,23 +763,23 @@ describe('Tracekit - Original Tests', () => { it('should parse Opera 25 error', () => { const stackFrames = computeStackTrace(OPERA_25); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ url: 'http://path/to/file.js', func: '?', args: [], line: 47, column: 22, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 52, column: 15, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: 'bar', args: [], @@ -792,23 +790,23 @@ describe('Tracekit - Original Tests', () => { it('should parse PhantomJS 1.19 error', () => { const stackFrames = computeStackTrace(PHANTOMJS_1_19); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ url: 'file:///path/to/file.js', func: '?', args: [], line: 878, column: null, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://path/to/file.js', func: 'foo', args: [], line: 4283, column: null, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://path/to/file.js', func: '?', args: [], @@ -819,9 +817,9 @@ describe('Tracekit - Original Tests', () => { it('should parse Firefox errors with resource: URLs', () => { const stackFrames = computeStackTrace(FIREFOX_50_RESOURCE_URL); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(3); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ url: 'resource://path/data/content/bundle.js', func: 'render', args: [], @@ -832,37 +830,37 @@ describe('Tracekit - Original Tests', () => { it('should parse Firefox errors with eval URLs', () => { const stackFrames = computeStackTrace(FIREFOX_43_EVAL); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(5); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(5); + expect(stackFrames.stack[0]).toEqual({ url: 'http://localhost:8080/file.js', func: 'baz', args: [], line: 26, column: null, }); - expect(stackFrames.stack[1]).to.deep.equal({ + expect(stackFrames.stack[1]).toEqual({ url: 'http://localhost:8080/file.js', func: 'foo', args: [], line: 26, column: null, }); - expect(stackFrames.stack[2]).to.deep.equal({ + expect(stackFrames.stack[2]).toEqual({ url: 'http://localhost:8080/file.js', func: 'eval', args: [], line: 26, column: null, }); - expect(stackFrames.stack[3]).to.deep.equal({ + expect(stackFrames.stack[3]).toEqual({ url: 'http://localhost:8080/file.js', func: 'speak', args: [], line: 26, column: 17, }); - expect(stackFrames.stack[4]).to.deep.equal({ + expect(stackFrames.stack[4]).toEqual({ url: 'http://localhost:8080/file.js', func: '?', args: [], @@ -873,16 +871,16 @@ describe('Tracekit - Original Tests', () => { it('should parse React Native errors on Android', () => { const stackFrames = computeStackTrace(ANDROID_REACT_NATIVE); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(8); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(8); + expect(stackFrames.stack[0]).toEqual({ url: '/home/username/sample-workspace/sampleapp.collect.react/src/components/GpsMonitorScene.js', func: 'render', args: [], line: 78, column: 24, }); - expect(stackFrames.stack[7]).to.deep.equal({ + expect(stackFrames.stack[7]).toEqual({ url: '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/native/ReactNativeBaseComponent.js', func: 'this', @@ -894,23 +892,23 @@ describe('Tracekit - Original Tests', () => { it('should parse React Native errors on Android Production', () => { const stackFrames = computeStackTrace(ANDROID_REACT_NATIVE_PROD); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(37); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(37); + expect(stackFrames.stack[0]).toEqual({ url: 'index.android.bundle', func: 'value', args: [], line: 12, column: 1917, }); - expect(stackFrames.stack[35]).to.deep.equal({ + expect(stackFrames.stack[35]).toEqual({ url: 'index.android.bundle', func: 'value', args: [], line: 29, column: 927, }); - expect(stackFrames.stack[36]).to.deep.equal({ + expect(stackFrames.stack[36]).toEqual({ url: '[native code]', func: '?', args: [], @@ -921,16 +919,16 @@ describe('Tracekit - Original Tests', () => { it('should parse React Native errors on Android Hermes', () => { const stackFrames = computeStackTrace(ANDROID_REACT_NATIVE_HERMES); - expect(stackFrames).to.be.ok; - expect(stackFrames.stack.length).to.equal(26); - expect(stackFrames.stack[0]).to.deep.equal({ + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(26); + expect(stackFrames.stack[0]).toEqual({ url: 'index.android.bundle', func: 'onPress', args: [], line: 1, column: 452701, }); - expect(stackFrames.stack[3]).to.deep.equal({ + expect(stackFrames.stack[3]).toEqual({ url: 'native', func: '_receiveSignal', args: ['native'], diff --git a/packages/browser/test/unit/transports/base.test.ts b/packages/browser/test/unit/transports/base.test.ts index 55ef2230c002..22a38b514135 100644 --- a/packages/browser/test/unit/transports/base.test.ts +++ b/packages/browser/test/unit/transports/base.test.ts @@ -1,5 +1,3 @@ -import { expect } from 'chai'; - import { BaseTransport } from '../../../src/transports/base'; const testDsn = 'https://123@sentry.io/42'; @@ -7,19 +5,19 @@ const testDsn = 'https://123@sentry.io/42'; class SimpleTransport extends BaseTransport {} describe('BaseTransport', () => { - it('doesnt provide sendEvent() implementation', async () => { + it('doesnt provide sendEvent() implementation', () => { const transport = new SimpleTransport({ dsn: testDsn }); try { - await transport.sendEvent({}); + void transport.sendEvent({}); } catch (e) { - expect(e.message).equal('Transport Class has to implement `sendEvent` method'); + expect(e.message).toBe('Transport Class has to implement `sendEvent` method'); } }); it('has correct endpoint url', () => { const transport = new SimpleTransport({ dsn: testDsn }); // eslint-disable-next-line deprecation/deprecation - expect(transport.url).equal('https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7'); + expect(transport.url).toBe('https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7'); }); }); diff --git a/packages/browser/test/unit/transports/fetch.test.ts b/packages/browser/test/unit/transports/fetch.test.ts index d1a1048c43f5..7f084cb7aaf4 100644 --- a/packages/browser/test/unit/transports/fetch.test.ts +++ b/packages/browser/test/unit/transports/fetch.test.ts @@ -1,7 +1,4 @@ -import { expect } from 'chai'; -import { SinonStub, stub } from 'sinon'; - -import { Event, Status, Transports } from '../../../src'; +import { Event, Response, Status, Transports } from '../../../src'; const testDsn = 'https://123@sentry.io/42'; const storeUrl = 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7'; @@ -14,81 +11,96 @@ const transactionPayload: Event = { type: 'transaction', }; -let fetch: SinonStub; +const fetch = jest.fn(); let transport: Transports.BaseTransport; +// eslint-disable-next-line no-var +declare var window: any; + +jest.mock('@sentry/utils', () => { + return { + ...jest.requireActual('@sentry/utils'), + supportsReferrerPolicy(): boolean { + return true; + }, + }; +}); + describe('FetchTransport', () => { beforeEach(() => { - fetch = (stub(window, 'fetch') as unknown) as SinonStub; + window.fetch = fetch; + window.Headers = class Headers { + headers: { [key: string]: string } = {}; + get(key: string) { + return this.headers[key]; + } + set(key: string, value: string) { + this.headers[key] = value; + } + }; transport = new Transports.FetchTransport({ dsn: testDsn }, window.fetch); }); afterEach(() => { - fetch.restore(); + fetch.mockRestore(); }); it('inherits composeEndpointUrl() implementation', () => { // eslint-disable-next-line deprecation/deprecation - expect(transport.url).equal(storeUrl); + expect(transport.url).toBe(storeUrl); }); - describe('sendEvent()', async () => { + describe('sendEvent()', () => { it('sends a request to Sentry servers', async () => { const response = { status: 200, headers: new Headers() }; - fetch.returns(Promise.resolve(response)); + window.fetch.mockImplementation(() => Promise.resolve(response)); const res = await transport.sendEvent(eventPayload); - expect(res.status).equal(Status.Success); - expect(fetch.calledOnce).equal(true); - expect( - fetch.calledWith(storeUrl, { - body: JSON.stringify(eventPayload), - method: 'POST', - referrerPolicy: 'origin', - }), - ).equal(true); + expect((res as Response).status).toBe(Status.Success); + expect(fetch).toHaveBeenCalledWith(storeUrl, { + body: JSON.stringify(eventPayload), + method: 'POST', + referrerPolicy: 'origin', + }); }); it('sends a request to tunnel if configured', async () => { transport = new Transports.FetchTransport({ dsn: testDsn, tunnel }, window.fetch); - fetch.returns(Promise.resolve({ status: 200, headers: new Headers() })); + window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); await transport.sendEvent(eventPayload); - expect(fetch.calledWith(tunnel)).equal(true); + expect(fetch.mock.calls[0][0]).toBe(tunnel); }); it('rejects with non-200 status code', async () => { const response = { status: 403, headers: new Headers() }; - fetch.returns(Promise.resolve(response)); + window.fetch.mockImplementation(() => Promise.resolve(response)); try { await transport.sendEvent(eventPayload); } catch (res) { - expect(res.status).equal(403); - expect(fetch.calledOnce).equal(true); - expect( - fetch.calledWith(storeUrl, { - body: JSON.stringify(eventPayload), - method: 'POST', - referrerPolicy: 'origin', - }), - ).equal(true); + expect((res as Response).status).toBe(403); + expect(fetch).toHaveBeenCalledWith(storeUrl, { + body: JSON.stringify(eventPayload), + method: 'POST', + referrerPolicy: 'origin', + }); } }); it('pass the error to rejection when fetch fails', async () => { const response = { status: 403, headers: new Headers() }; - fetch.returns(Promise.reject(response)); + window.fetch.mockImplementation(() => Promise.reject(response)); try { await transport.sendEvent(eventPayload); } catch (res) { - expect(res).equal(response); + expect(res).toBe(response); } }); @@ -104,21 +116,19 @@ describe('FetchTransport', () => { ); const response = { status: 200, headers: new Headers() }; - fetch.returns(Promise.resolve(response)); + window.fetch.mockImplementation(() => Promise.resolve(response)); const res = await transport.sendEvent(eventPayload); - expect(res.status).equal(Status.Success); - expect( - fetch.calledWith(storeUrl, { - body: JSON.stringify(eventPayload), - headers: { - Accept: 'application/json', - }, - method: 'POST', - referrerPolicy: 'origin', - }), - ).equal(true); + expect((res as Response).status).toBe(Status.Success); + expect(fetch).toHaveBeenCalledWith(storeUrl, { + body: JSON.stringify(eventPayload), + headers: { + Accept: 'application/json', + }, + method: 'POST', + referrerPolicy: 'origin', + }); }); it('passes in fetch parameters', async () => { @@ -133,19 +143,17 @@ describe('FetchTransport', () => { ); const response = { status: 200, headers: new Headers() }; - fetch.returns(Promise.resolve(response)); + window.fetch.mockImplementation(() => Promise.resolve(response)); const res = await transport.sendEvent(eventPayload); - expect(res.status).equal(Status.Success); - expect( - fetch.calledWith(storeUrl, { - body: JSON.stringify(eventPayload), - credentials: 'include', - method: 'POST', - referrerPolicy: 'origin', - }), - ).equal(true); + expect((res as Response).status).toBe(Status.Success); + expect(fetch).toHaveBeenCalledWith(storeUrl, { + body: JSON.stringify(eventPayload), + credentials: 'include', + method: 'POST', + referrerPolicy: 'origin', + }); }); describe('Rate-limiting', () => { @@ -155,54 +163,48 @@ describe('FetchTransport', () => { const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; const afterLimit = beforeLimit + retryAfterSeconds * 1000; - const dateStub = stub(Date, 'now') + jest + .spyOn(Date, 'now') // 1st event - _isRateLimited - false - .onCall(0) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 1st event - _handleRateLimit - .onCall(1) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 2nd event - _isRateLimited - true - .onCall(2) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 3rd event - _isRateLimited - false - .onCall(3) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 3rd event - _handleRateLimit - .onCall(4) - .returns(afterLimit); + .mockImplementationOnce(() => afterLimit); const headers = new Headers(); headers.set('Retry-After', `${retryAfterSeconds}`); - fetch.returns(Promise.resolve({ status: 429, headers })); + window.fetch.mockImplementation(() => Promise.resolve({ status: 429, headers })); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal(undefined); - expect(fetch.calledOnce).equal(true); + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBeUndefined(); + expect(fetch).toHaveBeenCalled(); } try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(fetch.calledOnce).equal(true); + expect(fetch).toHaveBeenCalled(); } - fetch.returns(Promise.resolve({ status: 200, headers: new Headers() })); + window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(fetch.calledTwice).equal(true); - - dateStub.restore(); + expect(eventRes.status).toBe(Status.Success); + expect(fetch).toHaveBeenCalledTimes(2); }); it('back-off using X-Sentry-Rate-Limits with single category', async () => { @@ -211,64 +213,56 @@ describe('FetchTransport', () => { const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; const afterLimit = beforeLimit + retryAfterSeconds * 1000; - const dateStub = stub(Date, 'now') + jest + .spyOn(Date, 'now') // 1st event - _isRateLimited - false - .onCall(0) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 1st event - _handleRateLimit - .onCall(1) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 2nd event - _isRateLimited - false (different category) - .onCall(2) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 2nd event - _handleRateLimit - .onCall(3) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 3rd event - _isRateLimited - true - .onCall(4) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 4th event - _isRateLimited - false - .onCall(5) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 4th event - _handleRateLimit - .onCall(6) - .returns(afterLimit); + .mockImplementationOnce(() => afterLimit); const headers = new Headers(); headers.set('X-Sentry-Rate-Limits', `${retryAfterSeconds}:error:scope`); - fetch.returns(Promise.resolve({ status: 429, headers })); + window.fetch.mockImplementation(() => Promise.resolve({ status: 429, headers })); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal(undefined); - expect(fetch.calledOnce).equal(true); + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBeUndefined(); + expect(fetch).toHaveBeenCalled(); } - fetch.returns(Promise.resolve({ status: 200, headers: new Headers() })); + window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).equal(Status.Success); - expect(fetch.calledTwice).equal(true); + expect(transactionRes.status).toBe(Status.Success); + expect(fetch).toHaveBeenCalledTimes(2); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(fetch.calledTwice).equal(true); + expect(fetch).toHaveBeenCalledTimes(2); } const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(fetch.calledThrice).equal(true); - - dateStub.restore(); + expect(eventRes.status).toBe(Status.Success); + expect(fetch).toHaveBeenCalledTimes(3); }); it('back-off using X-Sentry-Rate-Limits with multiple categories', async () => { @@ -277,78 +271,69 @@ describe('FetchTransport', () => { const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; const afterLimit = beforeLimit + retryAfterSeconds * 1000; - const dateStub = stub(Date, 'now') + jest + .spyOn(Date, 'now') // 1st event - _isRateLimited - false - .onCall(0) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 1st event - _handleRateLimit - .onCall(1) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 2nd event - _isRateLimited - true (event category) - .onCall(2) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 3rd event - _isRateLimited - true (transaction category) - .onCall(3) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 4th event - _isRateLimited - false (event category) - .onCall(4) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 4th event - _handleRateLimit - .onCall(5) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 5th event - _isRateLimited - false (transaction category) - .onCall(6) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 5th event - _handleRateLimit - .onCall(7) - .returns(afterLimit); + .mockImplementationOnce(() => afterLimit); const headers = new Headers(); headers.set('X-Sentry-Rate-Limits', `${retryAfterSeconds}:error;transaction:scope`); - fetch.returns(Promise.resolve({ status: 429, headers })); + window.fetch.mockImplementation(() => Promise.resolve({ status: 429, headers })); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal(undefined); - expect(fetch.calledOnce).equal(true); + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBeUndefined(); + expect(fetch).toHaveBeenCalled(); } try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(fetch.calledOnce).equal(true); + expect(fetch).toHaveBeenCalled(); } try { await transport.sendEvent(transactionPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for transaction requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(fetch.calledOnce).equal(true); + expect(fetch).toHaveBeenCalled(); } - fetch.returns(Promise.resolve({ status: 200, headers: new Headers() })); + window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(fetch.calledTwice).equal(true); + expect(eventRes.status).toBe(Status.Success); + expect(fetch).toHaveBeenCalledTimes(2); const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).equal(Status.Success); - expect(fetch.calledThrice).equal(true); - - dateStub.restore(); + expect(transactionRes.status).toBe(Status.Success); + expect(fetch).toHaveBeenCalledTimes(3); }); it('back-off using X-Sentry-Rate-Limits with missing categories should lock them all', async () => { @@ -357,78 +342,69 @@ describe('FetchTransport', () => { const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; const afterLimit = beforeLimit + retryAfterSeconds * 1000; - const dateStub = stub(Date, 'now') + jest + .spyOn(Date, 'now') // 1st event - _isRateLimited - false - .onCall(0) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 1st event - _handleRateLimit - .onCall(1) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 2nd event - _isRateLimited - true (event category) - .onCall(2) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 3rd event - _isRateLimited - true (transaction category) - .onCall(3) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 4th event - _isRateLimited - false (event category) - .onCall(4) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 4th event - _handleRateLimit - .onCall(5) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 5th event - _isRateLimited - false (transaction category) - .onCall(6) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 5th event - _handleRateLimit - .onCall(7) - .returns(afterLimit); + .mockImplementationOnce(() => afterLimit); const headers = new Headers(); headers.set('X-Sentry-Rate-Limits', `${retryAfterSeconds}::scope`); - fetch.returns(Promise.resolve({ status: 429, headers })); + window.fetch.mockImplementation(() => Promise.resolve({ status: 429, headers })); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal(undefined); - expect(fetch.calledOnce).equal(true); + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBeUndefined(); + expect(fetch).toHaveBeenCalled(); } try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(fetch.calledOnce).equal(true); + expect(fetch).toHaveBeenCalled(); } try { await transport.sendEvent(transactionPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for transaction requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(fetch.calledOnce).equal(true); + expect(fetch).toHaveBeenCalled(); } - fetch.returns(Promise.resolve({ status: 200, headers: new Headers() })); + window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(fetch.calledTwice).equal(true); + expect(eventRes.status).toBe(Status.Success); + expect(fetch).toHaveBeenCalledTimes(2); const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).equal(Status.Success); - expect(fetch.calledThrice).equal(true); - - dateStub.restore(); + expect(transactionRes.status).toBe(Status.Success); + expect(fetch).toHaveBeenCalledTimes(3); }); it('back-off using X-Sentry-Rate-Limits should also trigger for 200 responses', async () => { @@ -437,49 +413,43 @@ describe('FetchTransport', () => { const withinLimit = beforeLimit + (retryAfterSeconds / 2) * 1000; const afterLimit = beforeLimit + retryAfterSeconds * 1000; - const dateStub = stub(Date, 'now') + jest + .spyOn(Date, 'now') // 1st event - _isRateLimited - false - .onCall(0) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 1st event - _handleRateLimit - .onCall(1) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 2nd event - _isRateLimited - true - .onCall(2) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 3rd event - _isRateLimited - false - .onCall(3) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 3rd event - _handleRateLimit - .onCall(4) - .returns(afterLimit); + .mockImplementationOnce(() => afterLimit); const headers = new Headers(); headers.set('X-Sentry-Rate-Limits', `${retryAfterSeconds}:error;transaction:scope`); - fetch.returns(Promise.resolve({ status: 200, headers })); + window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers })); let eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(fetch.calledOnce).equal(true); + expect(eventRes.status).toBe(Status.Success); + expect(fetch).toHaveBeenCalled(); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(fetch.calledOnce).equal(true); + expect(fetch).toHaveBeenCalled(); } - fetch.returns(Promise.resolve({ status: 200, headers: new Headers() })); + window.fetch.mockImplementation(() => Promise.resolve({ status: 200, headers: new Headers() })); eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(fetch.calledTwice).equal(true); - - dateStub.restore(); + expect(eventRes.status).toBe(Status.Success); + expect(fetch).toHaveBeenCalledTimes(2); }); }); }); diff --git a/packages/browser/test/unit/transports/xhr.test.ts b/packages/browser/test/unit/transports/xhr.test.ts index be7da952710d..a41406998e20 100644 --- a/packages/browser/test/unit/transports/xhr.test.ts +++ b/packages/browser/test/unit/transports/xhr.test.ts @@ -1,7 +1,6 @@ -import { expect } from 'chai'; -import { fakeServer, SinonFakeServer, stub } from 'sinon'; +import { fakeServer, SinonFakeServer } from 'sinon'; -import { Event, Status, Transports } from '../../../src'; +import { Event, Response, Status, Transports } from '../../../src'; const testDsn = 'https://123@sentry.io/42'; const storeUrl = 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7'; @@ -31,20 +30,20 @@ describe('XHRTransport', () => { it('inherits composeEndpointUrl() implementation', () => { // eslint-disable-next-line deprecation/deprecation - expect(transport.url).equal(storeUrl); + expect(transport.url).toBe(storeUrl); }); - describe('sendEvent()', async () => { + describe('sendEvent()', () => { it('sends a request to Sentry servers', async () => { server.respondWith('POST', storeUrl, [200, {}, '']); const res = await transport.sendEvent(eventPayload); - expect(res.status).equal(Status.Success); + expect((res as Response).status).toBe(Status.Success); const request = server.requests[0]; - expect(server.requests.length).equal(1); - expect(request.method).equal('POST'); - expect(JSON.parse(request.requestBody)).deep.equal(eventPayload); + expect(server.requests.length).toBe(1); + expect(request.method).toBe('POST'); + expect(JSON.parse(request.requestBody)).toEqual(eventPayload); }); it('sends a request to tunnel if configured', async () => { @@ -53,7 +52,7 @@ describe('XHRTransport', () => { await transport.sendEvent(eventPayload); - expect(server.requests[0].url).equal(tunnel); + expect(server.requests[0].url).toBe(tunnel); }); it('rejects with non-200 status code', async () => { @@ -62,11 +61,11 @@ describe('XHRTransport', () => { try { await transport.sendEvent(eventPayload); } catch (res) { - expect(res.status).equal(403); + expect((res as Response).status).toBe(403); const request = server.requests[0]; - expect(server.requests.length).equal(1); - expect(request.method).equal('POST'); - expect(JSON.parse(request.requestBody)).deep.equal(eventPayload); + expect(server.requests.length).toBe(1); + expect(request.method).toBe('POST'); + expect(JSON.parse(request.requestBody)).toEqual(eventPayload); } }); @@ -82,9 +81,9 @@ describe('XHRTransport', () => { const res = await transport.sendEvent(eventPayload); const request = server.requests[0]; - expect(res.status).equal(Status.Success); + expect((res as Response).status).toBe(Status.Success); const requestHeaders: { [key: string]: string } = request.requestHeaders as { [key: string]: string }; - expect(requestHeaders['Accept']).equal('application/json'); + expect(requestHeaders['Accept']).toBe('application/json'); }); describe('Rate-limiting', () => { @@ -96,50 +95,44 @@ describe('XHRTransport', () => { server.respondWith('POST', storeUrl, [429, { 'Retry-After': `${retryAfterSeconds}` }, '']); - const dateStub = stub(Date, 'now') + jest + .spyOn(Date, 'now') // 1st event - _isRateLimited - false - .onCall(0) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 1st event - _handleRateLimit - .onCall(1) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 2nd event - _isRateLimited - true - .onCall(2) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 3rd event - _isRateLimited - false - .onCall(3) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 3rd event - _handleRateLimit - .onCall(4) - .returns(afterLimit); + .mockImplementationOnce(() => afterLimit); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal(undefined); - expect(server.requests.length).equal(1); + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBeUndefined(); + expect(server.requests.length).toBe(1); } try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(server.requests.length).equal(1); + expect(server.requests.length).toBe(1); } server.respondWith('POST', storeUrl, [200, {}, '']); const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(server.requests.length).equal(2); - - dateStub.restore(); + expect(eventRes.status).toBe(Status.Success); + expect(server.requests.length).toBe(2); }); it('back-off using X-Sentry-Rate-Limits with single category', async () => { @@ -151,60 +144,52 @@ describe('XHRTransport', () => { server.respondWith('POST', storeUrl, [429, { 'X-Sentry-Rate-Limits': `${retryAfterSeconds}:error:scope` }, '']); server.respondWith('POST', envelopeUrl, [200, {}, '']); - const dateStub = stub(Date, 'now') + jest + .spyOn(Date, 'now') // 1st event - _isRateLimited - false - .onCall(0) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 1st event - _handleRateLimit - .onCall(1) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 2nd event - _isRateLimited - false (different category) - .onCall(2) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 2nd event - _handleRateLimit - .onCall(3) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 3rd event - _isRateLimited - true - .onCall(4) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 4th event - _isRateLimited - false - .onCall(5) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 4th event - _handleRateLimit - .onCall(6) - .returns(afterLimit); + .mockImplementationOnce(() => afterLimit); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal(undefined); - expect(server.requests.length).equal(1); + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBeUndefined(); + expect(server.requests.length).toBe(1); } const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).equal(Status.Success); - expect(server.requests.length).equal(2); + expect(transactionRes.status).toBe(Status.Success); + expect(server.requests.length).toBe(2); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(server.requests.length).equal(2); + expect(server.requests.length).toBe(2); } server.respondWith('POST', storeUrl, [200, {}, '']); const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(server.requests.length).equal(3); - - dateStub.restore(); + expect(eventRes.status).toBe(Status.Success); + expect(server.requests.length).toBe(3); }); it('back-off using X-Sentry-Rate-Limits with multiple categories', async () => { @@ -220,75 +205,66 @@ describe('XHRTransport', () => { ]); server.respondWith('POST', envelopeUrl, [200, {}, '']); - const dateStub = stub(Date, 'now') + jest + .spyOn(Date, 'now') // 1st event - _isRateLimited - false - .onCall(0) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 1st event - _handleRateLimit - .onCall(1) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 2nd event - _isRateLimited - true (event category) - .onCall(2) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 3rd event - _isRateLimited - true (transaction category) - .onCall(3) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 4th event - _isRateLimited - false (event category) - .onCall(4) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 4th event - _handleRateLimit - .onCall(5) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 5th event - _isRateLimited - false (transaction category) - .onCall(6) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 5th event - _handleRateLimit - .onCall(7) - .returns(afterLimit); + .mockImplementationOnce(() => afterLimit); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal(undefined); - expect(server.requests.length).equal(1); + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBeUndefined(); + expect(server.requests.length).toBe(1); } try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(server.requests.length).equal(1); + expect(server.requests.length).toBe(1); } try { await transport.sendEvent(transactionPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for transaction requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(server.requests.length).equal(1); + expect(server.requests.length).toBe(1); } server.respondWith('POST', storeUrl, [200, {}, '']); server.respondWith('POST', envelopeUrl, [200, {}, '']); const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(server.requests.length).equal(2); + expect(eventRes.status).toBe(Status.Success); + expect(server.requests.length).toBe(2); const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).equal(Status.Success); - expect(server.requests.length).equal(3); - - dateStub.restore(); + expect(transactionRes.status).toBe(Status.Success); + expect(server.requests.length).toBe(3); }); it('back-off using X-Sentry-Rate-Limits with missing categories should lock them all', async () => { @@ -300,75 +276,66 @@ describe('XHRTransport', () => { server.respondWith('POST', storeUrl, [429, { 'X-Sentry-Rate-Limits': `${retryAfterSeconds}::scope` }, '']); server.respondWith('POST', envelopeUrl, [200, {}, '']); - const dateStub = stub(Date, 'now') + jest + .spyOn(Date, 'now') // 1st event - _isRateLimited - false - .onCall(0) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 1st event - _handleRateLimit - .onCall(1) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 2nd event - _isRateLimited - true (event category) - .onCall(2) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 3rd event - _isRateLimited - true (transaction category) - .onCall(3) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 4th event - _isRateLimited - false (event category) - .onCall(4) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 4th event - _handleRateLimit - .onCall(5) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 5th event - _isRateLimited - false (transaction category) - .onCall(6) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 5th event - _handleRateLimit - .onCall(7) - .returns(afterLimit); + .mockImplementationOnce(() => afterLimit); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal(undefined); - expect(server.requests.length).equal(1); + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBeUndefined(); + expect(server.requests.length).toBe(1); } try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(server.requests.length).equal(1); + expect(server.requests.length).toBe(1); } try { await transport.sendEvent(transactionPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for transaction requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(server.requests.length).equal(1); + expect(server.requests.length).toBe(1); } server.respondWith('POST', storeUrl, [200, {}, '']); server.respondWith('POST', envelopeUrl, [200, {}, '']); const eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(server.requests.length).equal(2); + expect(eventRes.status).toBe(Status.Success); + expect(server.requests.length).toBe(2); const transactionRes = await transport.sendEvent(transactionPayload); - expect(transactionRes.status).equal(Status.Success); - expect(server.requests.length).equal(3); - - dateStub.restore(); + expect(transactionRes.status).toBe(Status.Success); + expect(server.requests.length).toBe(3); }); it('back-off using X-Sentry-Rate-Limits should also trigger for 200 responses', async () => { @@ -379,45 +346,39 @@ describe('XHRTransport', () => { server.respondWith('POST', storeUrl, [200, { 'X-Sentry-Rate-Limits': `${retryAfterSeconds}:error:scope` }, '']); - const dateStub = stub(Date, 'now') + jest + .spyOn(Date, 'now') // 1st event - _isRateLimited - false - .onCall(0) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 1st event - _handleRateLimit - .onCall(1) - .returns(beforeLimit) + .mockImplementationOnce(() => beforeLimit) // 2nd event - _isRateLimited - true - .onCall(2) - .returns(withinLimit) + .mockImplementationOnce(() => withinLimit) // 3rd event - _isRateLimited - false - .onCall(3) - .returns(afterLimit) + .mockImplementationOnce(() => afterLimit) // 3rd event - _handleRateLimit - .onCall(4) - .returns(afterLimit); + .mockImplementationOnce(() => afterLimit); let eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(server.requests.length).equal(1); + expect(eventRes.status).toBe(Status.Success); + expect(server.requests.length).toBe(1); try { await transport.sendEvent(eventPayload); throw new Error('unreachable!'); } catch (res) { - expect(res.status).equal(429); - expect(res.reason).equal( + expect((res as Response).status).toBe(429); + expect((res as Response).reason).toBe( `Transport for event requests locked till ${new Date(afterLimit)} due to too many requests.`, ); - expect(server.requests.length).equal(1); + expect(server.requests.length).toBe(1); } server.respondWith('POST', storeUrl, [200, {}, '']); eventRes = await transport.sendEvent(eventPayload); - expect(eventRes.status).equal(Status.Success); - expect(server.requests.length).equal(2); - - dateStub.restore(); + expect(eventRes.status).toBe(Status.Success); + expect(server.requests.length).toBe(2); }); }); }); diff --git a/packages/browser/tsconfig.json b/packages/browser/tsconfig.json index 15c1a0af3c44..a53a9d679770 100644 --- a/packages/browser/tsconfig.json +++ b/packages/browser/tsconfig.json @@ -4,6 +4,6 @@ "exclude": ["dist"], "compilerOptions": { "rootDir": ".", - "types": ["node", "mocha", "chai", "sinon"] + "types": ["node", "mocha", "chai", "sinon", "jest"] } }