diff --git a/packages/browser/jest.config.js b/packages/browser/jest.config.js deleted file mode 100644 index f9cd8056a454..000000000000 --- a/packages/browser/jest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const baseConfig = require('../../jest/jest.config.js'); - -module.exports = { - ...baseConfig, - testEnvironment: 'jsdom', - testMatch: ['/test/unit/**/*.test.ts'], -}; diff --git a/packages/browser/package.json b/packages/browser/package.json index ff719729e534..221f8a492a02 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -70,9 +70,8 @@ "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", "size:check": "cat build/bundles/bundle.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES2017: \",$1,\"kB\";}'", - "test": "yarn test:unit", - "test:unit": "jest", - "test:unit:watch": "jest --watch", + "test": "vitest run", + "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" }, "volta": { diff --git a/packages/browser/test/unit/eventbuilder.test.ts b/packages/browser/test/eventbuilder.test.ts similarity index 92% rename from packages/browser/test/unit/eventbuilder.test.ts rename to packages/browser/test/eventbuilder.test.ts index e28b283107c3..0f43e7495efd 100644 --- a/packages/browser/test/unit/eventbuilder.test.ts +++ b/packages/browser/test/eventbuilder.test.ts @@ -1,10 +1,15 @@ -import { defaultStackParser } from '../../src'; -import { eventFromUnknownInput } from '../../src/eventbuilder'; +/** + * @vitest-environment jsdom + */ -jest.mock('@sentry/core', () => { - const original = jest.requireActual('@sentry/core'); +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import { defaultStackParser } from '../src'; +import { eventFromUnknownInput } from '../src/eventbuilder'; + +vi.mock('@sentry/core', async requireActual => { return { - ...original, + ...((await requireActual()) as any), getClient() { return { getOptions(): any { @@ -21,7 +26,7 @@ class MyTestClass { } afterEach(() => { - jest.resetAllMocks(); + vi.resetAllMocks(); }); describe('eventFromUnknownInput', () => { diff --git a/packages/browser/test/unit/helper/browser-client-options.ts b/packages/browser/test/helper/browser-client-options.ts similarity index 87% rename from packages/browser/test/unit/helper/browser-client-options.ts rename to packages/browser/test/helper/browser-client-options.ts index 867e6a9e6e6e..619baab13bad 100644 --- a/packages/browser/test/unit/helper/browser-client-options.ts +++ b/packages/browser/test/helper/browser-client-options.ts @@ -1,7 +1,7 @@ import { createTransport } from '@sentry/core'; import { resolvedSyncPromise } from '@sentry/utils'; -import type { BrowserClientOptions } from '../../../src/client'; +import type { BrowserClientOptions } from '../../src/client'; export function getDefaultBrowserClientOptions(options: Partial = {}): BrowserClientOptions { return { diff --git a/packages/browser/test/unit/index.bundle.feedback.test.ts b/packages/browser/test/index.bundle.feedback.test.ts similarity index 76% rename from packages/browser/test/unit/index.bundle.feedback.test.ts rename to packages/browser/test/index.bundle.feedback.test.ts index 99516cca295e..47beaa33d07c 100644 --- a/packages/browser/test/unit/index.bundle.feedback.test.ts +++ b/packages/browser/test/index.bundle.feedback.test.ts @@ -1,7 +1,9 @@ +import { describe, expect, it } from 'vitest'; + import { browserTracingIntegrationShim, replayIntegrationShim } from '@sentry-internal/integration-shims'; -import { feedbackAsyncIntegration } from '../../src'; +import { feedbackAsyncIntegration } from '../src'; -import * as FeedbackBundle from '../../src/index.bundle.feedback'; +import * as FeedbackBundle from '../src/index.bundle.feedback'; describe('index.bundle.feedback', () => { it('has correct exports', () => { diff --git a/packages/browser/test/unit/index.bundle.replay.test.ts b/packages/browser/test/index.bundle.replay.test.ts similarity index 77% rename from packages/browser/test/unit/index.bundle.replay.test.ts rename to packages/browser/test/index.bundle.replay.test.ts index 0fdbf95fe3e4..7efbe9ffb5b8 100644 --- a/packages/browser/test/unit/index.bundle.replay.test.ts +++ b/packages/browser/test/index.bundle.replay.test.ts @@ -1,7 +1,9 @@ +import { describe, expect, it } from 'vitest'; + import { browserTracingIntegrationShim, feedbackIntegrationShim } from '@sentry-internal/integration-shims'; -import { replayIntegration } from '../../src'; +import { replayIntegration } from '../src'; -import * as ReplayBundle from '../../src/index.bundle.replay'; +import * as ReplayBundle from '../src/index.bundle.replay'; describe('index.bundle.replay', () => { it('has correct exports', () => { diff --git a/packages/browser/test/unit/index.bundle.test.ts b/packages/browser/test/index.bundle.test.ts similarity index 84% rename from packages/browser/test/unit/index.bundle.test.ts rename to packages/browser/test/index.bundle.test.ts index 1535d74d6b6a..4b4618e2f4eb 100644 --- a/packages/browser/test/unit/index.bundle.test.ts +++ b/packages/browser/test/index.bundle.test.ts @@ -1,10 +1,12 @@ +import { describe, expect, it } from 'vitest'; + import { browserTracingIntegrationShim, feedbackIntegrationShim, replayIntegrationShim, } from '@sentry-internal/integration-shims'; -import * as Bundle from '../../src/index.bundle'; +import * as Bundle from '../src/index.bundle'; describe('index.bundle', () => { it('has correct exports', () => { diff --git a/packages/browser/test/unit/index.bundle.tracing.replay.feedback.test.ts b/packages/browser/test/index.bundle.tracing.replay.feedback.test.ts similarity index 75% rename from packages/browser/test/unit/index.bundle.tracing.replay.feedback.test.ts rename to packages/browser/test/index.bundle.tracing.replay.feedback.test.ts index 2d62f247a1da..e5a454b532e9 100644 --- a/packages/browser/test/unit/index.bundle.tracing.replay.feedback.test.ts +++ b/packages/browser/test/index.bundle.tracing.replay.feedback.test.ts @@ -1,6 +1,8 @@ -import { browserTracingIntegration, feedbackAsyncIntegration, replayIntegration } from '../../src'; +import { describe, expect, it } from 'vitest'; -import * as TracingReplayFeedbackBundle from '../../src/index.bundle.tracing.replay.feedback'; +import { browserTracingIntegration, feedbackAsyncIntegration, replayIntegration } from '../src'; + +import * as TracingReplayFeedbackBundle from '../src/index.bundle.tracing.replay.feedback'; describe('index.bundle.tracing.replay.feedback', () => { it('has correct exports', () => { diff --git a/packages/browser/test/unit/index.bundle.tracing.replay.test.ts b/packages/browser/test/index.bundle.tracing.replay.test.ts similarity index 81% rename from packages/browser/test/unit/index.bundle.tracing.replay.test.ts rename to packages/browser/test/index.bundle.tracing.replay.test.ts index 2cd3f5dca0f0..fa81d1b19aae 100644 --- a/packages/browser/test/unit/index.bundle.tracing.replay.test.ts +++ b/packages/browser/test/index.bundle.tracing.replay.test.ts @@ -1,7 +1,9 @@ +import { describe, expect, it } from 'vitest'; + import { feedbackIntegrationShim } from '@sentry-internal/integration-shims'; -import { browserTracingIntegration, replayIntegration } from '../../src'; +import { browserTracingIntegration, replayIntegration } from '../src'; -import * as TracingReplayBundle from '../../src/index.bundle.tracing.replay'; +import * as TracingReplayBundle from '../src/index.bundle.tracing.replay'; describe('index.bundle.tracing.replay', () => { it('has correct exports', () => { diff --git a/packages/browser/test/unit/index.bundle.tracing.test.ts b/packages/browser/test/index.bundle.tracing.test.ts similarity index 75% rename from packages/browser/test/unit/index.bundle.tracing.test.ts rename to packages/browser/test/index.bundle.tracing.test.ts index 942d185b2e91..dc786fd31a61 100644 --- a/packages/browser/test/unit/index.bundle.tracing.test.ts +++ b/packages/browser/test/index.bundle.tracing.test.ts @@ -1,7 +1,9 @@ +import { describe, expect, it } from 'vitest'; + import { feedbackIntegrationShim, replayIntegrationShim } from '@sentry-internal/integration-shims'; -import { browserTracingIntegration } from '../../src'; +import { browserTracingIntegration } from '../src'; -import * as TracingBundle from '../../src/index.bundle.tracing'; +import * as TracingBundle from '../src/index.bundle.tracing'; describe('index.bundle.tracing', () => { it('has correct exports', () => { diff --git a/packages/browser/test/unit/index.test.ts b/packages/browser/test/index.test.ts similarity index 83% rename from packages/browser/test/unit/index.test.ts rename to packages/browser/test/index.test.ts index 34c4845cac13..eb2fb6104b11 100644 --- a/packages/browser/test/unit/index.test.ts +++ b/packages/browser/test/index.test.ts @@ -1,3 +1,10 @@ +/** + * @vitest-environment jsdom + */ + +import type { Mock } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + import { SDK_VERSION, getGlobalScope, @@ -8,7 +15,7 @@ import { } from '@sentry/core'; import * as utils from '@sentry/utils'; -import { setCurrentClient } from '../../src'; +import { setCurrentClient } from '../src'; import { BrowserClient, Scope, @@ -22,7 +29,7 @@ import { getCurrentScope, init, showReportDialog, -} from '../../src'; +} from '../src'; import { getDefaultBrowserClientOptions } from './helper/browser-client-options'; import { makeSimpleTransport } from './mocks/simpletransport'; @@ -31,16 +38,15 @@ const dsn = 'https://53039209a22b4ec1bcc296a3c9fdecd6@sentry.io/4291'; // eslint-disable-next-line no-var declare var global: any; -jest.mock('@sentry/core', () => { - const original = jest.requireActual('@sentry/core'); +vi.mock('@sentry/core', async requireActual => { return { - ...original, - getReportDialogEndpoint: jest.fn(), + ...((await requireActual()) as any), + getReportDialogEndpoint: vi.fn(), }; }); describe('SentryBrowser', () => { - const beforeSend = jest.fn(event => event); + const beforeSend = vi.fn(event => event); beforeEach(() => { getGlobalScope().clear(); @@ -84,7 +90,7 @@ describe('SentryBrowser', () => { describe('showReportDialog', () => { beforeEach(() => { - (getReportDialogEndpoint as jest.Mock).mockReset(); + (getReportDialogEndpoint as Mock).mockReset(); }); describe('user', () => { @@ -145,14 +151,14 @@ describe('SentryBrowser', () => { }); describe('onClose', () => { - const dummyErrorHandler = jest.fn(); + const dummyErrorHandler = vi.fn(); beforeEach(() => { - // this prevents jest-environment-jsdom from failing the test + // this prevents vi-environment-jsdom from failing the test // when an error in `onClose` is thrown // it does not prevent errors thrown directly inside the test, // so we don't have to worry about tests passing that should // otherwise fail - // see: https://github.com/jestjs/jest/blob/main/packages/jest-environment-jsdom/src/index.ts#L95-L115 + // see: https://github.com/vijs/vi/blob/main/packages/vi-environment-jsdom/src/index.ts#L95-L115 WINDOW.addEventListener('error', dummyErrorHandler); }); @@ -166,7 +172,7 @@ describe('SentryBrowser', () => { }; it('should call `onClose` when receiving `__sentry_reportdialog_closed__` MessageEvent', async () => { - const onClose = jest.fn(); + const onClose = vi.fn(); showReportDialog({ onClose }); @@ -179,7 +185,7 @@ describe('SentryBrowser', () => { }); it('should call `onClose` only once even if it throws', async () => { - const onClose = jest.fn(() => { + const onClose = vi.fn(() => { throw new Error(); }); @@ -194,7 +200,7 @@ describe('SentryBrowser', () => { }); it('should not call `onClose` for other MessageEvents', async () => { - const onClose = jest.fn(); + const onClose = vi.fn(); showReportDialog({ onClose }); @@ -236,49 +242,52 @@ describe('SentryBrowser', () => { expect(event.exception.values[0]?.stacktrace.frames).not.toHaveLength(0); }); - it('should capture a message', done => { - const options = getDefaultBrowserClientOptions({ - beforeSend: event => { - expect(event.message).toBe('test'); - expect(event.exception).toBeUndefined(); - done(); - return event; - }, - dsn, - }); - setCurrentClient(new BrowserClient(options)); - captureMessage('test'); - }); - - it('should capture an event', done => { - const options = getDefaultBrowserClientOptions({ - beforeSend: event => { - expect(event.message).toBe('event'); - expect(event.exception).toBeUndefined(); - done(); - return event; - }, - dsn, - }); - setCurrentClient(new BrowserClient(options)); - captureEvent({ message: 'event' }); - }); - - it('should set `platform` on events', done => { - const options = getDefaultBrowserClientOptions({ - beforeSend: event => { - expect(event.platform).toBe('javascript'); - done(); - return event; - }, - dsn, - }); - setCurrentClient(new BrowserClient(options)); - captureEvent({ message: 'event' }); - }); + it('should capture a message', () => + new Promise(resolve => { + const options = getDefaultBrowserClientOptions({ + beforeSend: event => { + expect(event.message).toBe('test'); + expect(event.exception).toBeUndefined(); + resolve(); + return event; + }, + dsn, + }); + setCurrentClient(new BrowserClient(options)); + captureMessage('test'); + })); + + it('should capture an event', () => + new Promise(resolve => { + const options = getDefaultBrowserClientOptions({ + beforeSend: event => { + expect(event.message).toBe('event'); + expect(event.exception).toBeUndefined(); + resolve(); + return event; + }, + dsn, + }); + setCurrentClient(new BrowserClient(options)); + captureEvent({ message: 'event' }); + })); + + it('should set `platform` on events', () => + new Promise(resolve => { + const options = getDefaultBrowserClientOptions({ + beforeSend: event => { + expect(event.platform).toBe('javascript'); + resolve(); + return event; + }, + dsn, + }); + setCurrentClient(new BrowserClient(options)); + captureEvent({ message: 'event' }); + })); it('should not dedupe an event on bound client', async () => { - const localBeforeSend = jest.fn(); + const localBeforeSend = vi.fn(); const options = getDefaultBrowserClientOptions({ beforeSend: localBeforeSend, dsn, @@ -295,7 +304,7 @@ describe('SentryBrowser', () => { }); it('should use inboundfilter rules of bound client', async () => { - const localBeforeSend = jest.fn(); + const localBeforeSend = vi.fn(); const options = getDefaultBrowserClientOptions({ beforeSend: localBeforeSend, dsn, @@ -374,7 +383,7 @@ describe('SentryBrowser initialization', () => { }); it('uses SDK source from global for package name', () => { - const spy = jest.spyOn(utils, 'getSDKSource').mockReturnValue('cdn'); + const spy = vi.spyOn(utils, 'getSDKSource').mockReturnValue('cdn'); init({ dsn }); const sdkData = getClient()?.getOptions()._metadata?.sdk || {}; diff --git a/packages/browser/test/unit/integrations/breadcrumbs.test.ts b/packages/browser/test/integrations/breadcrumbs.test.ts similarity index 82% rename from packages/browser/test/unit/integrations/breadcrumbs.test.ts rename to packages/browser/test/integrations/breadcrumbs.test.ts index 15ad5f3e4261..770578ef77b5 100644 --- a/packages/browser/test/unit/integrations/breadcrumbs.test.ts +++ b/packages/browser/test/integrations/breadcrumbs.test.ts @@ -1,6 +1,8 @@ +import { describe, expect, it, vi } from 'vitest'; + import * as SentryCore from '@sentry/core'; -import { BrowserClient, breadcrumbsIntegration, flush } from '../../../src'; +import { BrowserClient, breadcrumbsIntegration, flush } from '../../src'; import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; describe('Breadcrumbs', () => { @@ -14,7 +16,7 @@ describe('Breadcrumbs', () => { SentryCore.setCurrentClient(client); client.init(); - const addBreadcrumbSpy = jest.spyOn(SentryCore, 'addBreadcrumb').mockImplementation(() => {}); + const addBreadcrumbSpy = vi.spyOn(SentryCore, 'addBreadcrumb').mockImplementation(() => {}); client.captureMessage('test'); await flush(2000); diff --git a/packages/browser/test/unit/integrations/contextlines.test.ts b/packages/browser/test/integrations/contextlines.test.ts similarity index 96% rename from packages/browser/test/unit/integrations/contextlines.test.ts rename to packages/browser/test/integrations/contextlines.test.ts index 5f19bd2b41a7..783cc36fec89 100644 --- a/packages/browser/test/unit/integrations/contextlines.test.ts +++ b/packages/browser/test/integrations/contextlines.test.ts @@ -1,6 +1,8 @@ +import { describe, expect, it } from 'vitest'; + import type { StackFrame } from '@sentry/types'; -import { applySourceContextToFrame } from '../../../src/integrations/contextlines'; +import { applySourceContextToFrame } from '../../src/integrations/contextlines'; const lines = ['line1', 'line2', 'line3', 'line4', 'line5', 'line6', 'line7', 'line8', 'line9']; describe('ContextLines', () => { diff --git a/packages/browser/test/unit/integrations/helpers.test.ts b/packages/browser/test/integrations/helpers.test.ts similarity index 97% rename from packages/browser/test/unit/integrations/helpers.test.ts rename to packages/browser/test/integrations/helpers.test.ts index 080ab4c5cd0f..37806e06f8a9 100644 --- a/packages/browser/test/unit/integrations/helpers.test.ts +++ b/packages/browser/test/integrations/helpers.test.ts @@ -1,6 +1,8 @@ +import { describe, expect, it, vi } from 'vitest'; + import type { WrappedFunction } from '@sentry/types'; -import { wrap } from '../../../src/helpers'; +import { wrap } from '../../src/helpers'; describe('internal wrap()', () => { it('should wrap only functions', () => { @@ -56,7 +58,7 @@ describe('internal wrap()', () => { it('calls "before" function when invoking wrapped function', () => { const fn = (() => 1337) as WrappedFunction; - const before = jest.fn(); + const before = vi.fn(); const wrapped = wrap(fn, {}, before); wrapped(); @@ -115,7 +117,7 @@ describe('internal wrap()', () => { }); it('calls the original function', () => { - const fn = jest.fn(); + const fn = vi.fn(); wrap(fn)(123, 'Rick'); diff --git a/packages/browser/test/unit/integrations/reportingobserver.test.ts b/packages/browser/test/integrations/reportingobserver.test.ts similarity index 94% rename from packages/browser/test/unit/integrations/reportingobserver.test.ts rename to packages/browser/test/integrations/reportingobserver.test.ts index aba669286f44..9571d59d03d0 100644 --- a/packages/browser/test/unit/integrations/reportingobserver.test.ts +++ b/packages/browser/test/integrations/reportingobserver.test.ts @@ -1,20 +1,22 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + import * as SentryCore from '@sentry/core'; import type { Client } from '@sentry/types'; -import { reportingObserverIntegration } from '../../../src/integrations/reportingobserver'; +import { reportingObserverIntegration } from '../../src/integrations/reportingobserver'; const mockScope = { - setExtra: jest.fn(), + setExtra: vi.fn(), }; -const withScope = jest.fn(callback => { +const withScope = vi.fn(callback => { return callback(mockScope); }); -const captureMessage = jest.fn(); +const captureMessage = vi.fn(); -const mockReportingObserverConstructor = jest.fn(); -const mockObserve = jest.fn(); +const mockReportingObserverConstructor = vi.fn(); +const mockObserve = vi.fn(); class MockReportingObserver { public observe: () => void = mockObserve; @@ -32,13 +34,13 @@ describe('ReportingObserver', () => { mockClient = {} as Client; - jest.spyOn(SentryCore, 'captureMessage').mockImplementation(captureMessage); - jest.spyOn(SentryCore, 'getClient').mockImplementation(() => mockClient); - jest.spyOn(SentryCore, 'withScope').mockImplementation(withScope); + vi.spyOn(SentryCore, 'captureMessage').mockImplementation(captureMessage); + vi.spyOn(SentryCore, 'getClient').mockImplementation(() => mockClient); + vi.spyOn(SentryCore, 'withScope').mockImplementation(withScope); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); delete (global as any).ReportingObserver; }); diff --git a/packages/browser/test/unit/mocks/simpletransport.ts b/packages/browser/test/mocks/simpletransport.ts similarity index 100% rename from packages/browser/test/unit/mocks/simpletransport.ts rename to packages/browser/test/mocks/simpletransport.ts diff --git a/packages/browser/test/unit/profiling/integration.test.ts b/packages/browser/test/profiling/integration.test.ts similarity index 84% rename from packages/browser/test/unit/profiling/integration.test.ts rename to packages/browser/test/profiling/integration.test.ts index b5c4ad7c82a5..2f653563fc1a 100644 --- a/packages/browser/test/unit/profiling/integration.test.ts +++ b/packages/browser/test/profiling/integration.test.ts @@ -1,11 +1,17 @@ +/** + * @vitest-environment jsdom + */ + +import { describe, expect, it, vi } from 'vitest'; + import type { BrowserClient } from '@sentry/browser'; import * as Sentry from '@sentry/browser'; -import type { JSSelfProfile } from '../../../src/profiling/jsSelfProfiling'; +import type { JSSelfProfile } from '../../src/profiling/jsSelfProfiling'; describe('BrowserProfilingIntegration', () => { it('pageload profiles follow regular transaction code path', async () => { - const stopProfile = jest.fn().mockImplementation((): Promise => { + const stopProfile = vi.fn().mockImplementation((): Promise => { return Promise.resolve({ frames: [{ name: 'pageload_fn', line: 1, column: 1 }], stacks: [{ frameId: 0, parentId: undefined }], @@ -30,8 +36,8 @@ describe('BrowserProfilingIntegration', () => { // @ts-expect-error this is a mock constructor window.Profiler = MockProfiler; - const flush = jest.fn().mockImplementation(() => Promise.resolve(true)); - const send = jest.fn().mockImplementation(() => Promise.resolve()); + const flush = vi.fn().mockImplementation(() => Promise.resolve(true)); + const send = vi.fn().mockImplementation(() => Promise.resolve()); Sentry.init({ tracesSampleRate: 1, profilesSampleRate: 1, diff --git a/packages/browser/test/unit/profiling/utils.test.ts b/packages/browser/test/profiling/utils.test.ts similarity index 94% rename from packages/browser/test/unit/profiling/utils.test.ts rename to packages/browser/test/profiling/utils.test.ts index 573d9dbb335e..27e81c3208dc 100644 --- a/packages/browser/test/unit/profiling/utils.test.ts +++ b/packages/browser/test/profiling/utils.test.ts @@ -1,3 +1,9 @@ +/** + * @vitest-environment jsdom + */ + +import { afterAll, afterEach, beforeEach, describe, expect, it } from 'vitest'; + import { TextDecoder, TextEncoder } from 'util'; const patchedEncoder = (!global.window.TextEncoder && (global.window.TextEncoder = TextEncoder)) || true; // @ts-expect-error patch the encoder on the window, else importing JSDOM fails (deleted in afterAll) @@ -5,8 +11,8 @@ const patchedDecoder = (!global.window.TextDecoder && (global.window.TextDecoder import { JSDOM } from 'jsdom'; -import type { JSSelfProfile } from '../../../src/profiling/jsSelfProfiling'; -import { convertJSSelfProfileToSampledFormat } from '../../../src/profiling/utils'; +import type { JSSelfProfile } from '../../src/profiling/jsSelfProfiling'; +import { convertJSSelfProfileToSampledFormat } from '../../src/profiling/utils'; const makeJSProfile = (partial: Partial = {}): JSSelfProfile => { return { diff --git a/packages/browser/test/unit/sdk.test.ts b/packages/browser/test/sdk.test.ts similarity index 78% rename from packages/browser/test/unit/sdk.test.ts rename to packages/browser/test/sdk.test.ts index 667ad850fb36..31178bc84423 100644 --- a/packages/browser/test/unit/sdk.test.ts +++ b/packages/browser/test/sdk.test.ts @@ -1,11 +1,18 @@ +/** + * @vitest-environment jsdom + */ + /* eslint-disable @typescript-eslint/unbound-method */ +import type { Mock } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + import { Scope, createTransport } from '@sentry/core'; import type { Client, Integration } from '@sentry/types'; import { resolvedSyncPromise } from '@sentry/utils'; -import type { BrowserOptions } from '../../src'; -import { WINDOW } from '../../src'; -import { init } from '../../src/sdk'; +import type { BrowserOptions } from '../src'; +import { WINDOW } from '../src'; +import { init } from '../src/sdk'; const PUBLIC_DSN = 'https://username@domain/123'; @@ -20,16 +27,15 @@ function getDefaultBrowserOptions(options: Partial = {}): Browse export class MockIntegration implements Integration { public name: string; - public setupOnce: () => void = jest.fn(); + public setupOnce: () => void = vi.fn(); public constructor(name: string) { this.name = name; } } -jest.mock('@sentry/core', () => { - const original = jest.requireActual('@sentry/core'); +vi.mock('@sentry/core', async requireActual => { return { - ...original, + ...((await requireActual()) as any), getCurrentHub(): { bindClient(client: Client): boolean; getClient(): boolean; @@ -53,11 +59,11 @@ jest.mock('@sentry/core', () => { describe('init', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); afterAll(() => { - jest.resetAllMocks(); + vi.resetAllMocks(); }); test('installs default integrations', () => { @@ -69,8 +75,8 @@ describe('init', () => { init(options); - expect(DEFAULT_INTEGRATIONS[0]!.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); - expect(DEFAULT_INTEGRATIONS[1]!.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(DEFAULT_INTEGRATIONS[0]!.setupOnce as Mock).toHaveBeenCalledTimes(1); + expect(DEFAULT_INTEGRATIONS[1]!.setupOnce as Mock).toHaveBeenCalledTimes(1); }); test("doesn't install default integrations if told not to", () => { @@ -81,8 +87,8 @@ describe('init', () => { const options = getDefaultBrowserOptions({ dsn: PUBLIC_DSN, defaultIntegrations: false }); init(options); - expect(DEFAULT_INTEGRATIONS[0]!.setupOnce as jest.Mock).toHaveBeenCalledTimes(0); - expect(DEFAULT_INTEGRATIONS[1]!.setupOnce as jest.Mock).toHaveBeenCalledTimes(0); + expect(DEFAULT_INTEGRATIONS[0]!.setupOnce as Mock).toHaveBeenCalledTimes(0); + expect(DEFAULT_INTEGRATIONS[1]!.setupOnce as Mock).toHaveBeenCalledTimes(0); }); it('installs merged default integrations, with overrides provided through options', () => { @@ -100,10 +106,10 @@ describe('init', () => { init(options); // 'MockIntegration 1' should be overridden by the one with the same name provided through options - expect(DEFAULT_INTEGRATIONS[0]!.setupOnce as jest.Mock).toHaveBeenCalledTimes(0); - expect(DEFAULT_INTEGRATIONS[1]!.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); - expect(integrations[0]!.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); - expect(integrations[1]!.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(DEFAULT_INTEGRATIONS[0]!.setupOnce as Mock).toHaveBeenCalledTimes(0); + expect(DEFAULT_INTEGRATIONS[1]!.setupOnce as Mock).toHaveBeenCalledTimes(1); + expect(integrations[0]!.setupOnce as Mock).toHaveBeenCalledTimes(1); + expect(integrations[1]!.setupOnce as Mock).toHaveBeenCalledTimes(1); }); it('installs integrations returned from a callback function', () => { @@ -124,9 +130,9 @@ describe('init', () => { init(options); - expect(DEFAULT_INTEGRATIONS[0]!.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); - expect(newIntegration.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); - expect(DEFAULT_INTEGRATIONS[1]!.setupOnce as jest.Mock).toHaveBeenCalledTimes(0); + expect(DEFAULT_INTEGRATIONS[0]!.setupOnce as Mock).toHaveBeenCalledTimes(1); + expect(newIntegration.setupOnce as Mock).toHaveBeenCalledTimes(1); + expect(DEFAULT_INTEGRATIONS[1]!.setupOnce as Mock).toHaveBeenCalledTimes(0); }); describe('initialization error in browser extension', () => { @@ -146,7 +152,7 @@ describe('init', () => { }); it('logs a browser extension error if executed inside a Chrome extension', () => { - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); Object.defineProperty(WINDOW, 'chrome', { value: { runtime: { id: 'mock-extension-id' } }, @@ -164,7 +170,7 @@ describe('init', () => { }); it('logs a browser extension error if executed inside a Firefox/Safari extension', () => { - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); Object.defineProperty(WINDOW, 'browser', { value: { runtime: { id: 'mock-extension-id' } }, writable: true }); @@ -181,7 +187,7 @@ describe('init', () => { it.each(['chrome-extension', 'moz-extension', 'ms-browser-extension'])( "doesn't log a browser extension error if executed inside an extension running in a dedicated page (%s)", extensionProtocol => { - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); // @ts-expect-error - this is a hack to simulate a dedicated page in a browser extension delete WINDOW.location; @@ -202,7 +208,7 @@ describe('init', () => { ); it("doesn't log a browser extension error if executed inside regular browser environment", () => { - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); init(options); @@ -212,7 +218,7 @@ describe('init', () => { }); it("doesn't log a browser extension error if executed inside an NW.js environment", () => { - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); Object.defineProperty(WINDOW, 'nw', { value: {} }); @@ -224,7 +230,7 @@ describe('init', () => { }); it("doesn't return a client on initialization error", () => { - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); Object.defineProperty(WINDOW, 'chrome', { value: { runtime: { id: 'mock-extension-id' } }, diff --git a/packages/browser/test/unit/tracekit/chromium.test.ts b/packages/browser/test/tracekit/chromium.test.ts similarity index 99% rename from packages/browser/test/unit/tracekit/chromium.test.ts rename to packages/browser/test/tracekit/chromium.test.ts index 790f36e0ddc3..36d728f9cbea 100644 --- a/packages/browser/test/unit/tracekit/chromium.test.ts +++ b/packages/browser/test/tracekit/chromium.test.ts @@ -1,5 +1,7 @@ -import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParser as parser } from '../../../src/stack-parsers'; +import { describe, expect, it } from 'vitest'; + +import { exceptionFromError } from '../../src/eventbuilder'; +import { defaultStackParser as parser } from '../../src/stack-parsers'; describe('Tracekit - Chrome Tests', () => { it('should parse Chrome error with no location', () => { diff --git a/packages/browser/test/unit/tracekit/firefox.test.ts b/packages/browser/test/tracekit/firefox.test.ts similarity index 98% rename from packages/browser/test/unit/tracekit/firefox.test.ts rename to packages/browser/test/tracekit/firefox.test.ts index 5e05930f8078..4a0d3d90e7dc 100644 --- a/packages/browser/test/unit/tracekit/firefox.test.ts +++ b/packages/browser/test/tracekit/firefox.test.ts @@ -1,5 +1,7 @@ -import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParser as parser } from '../../../src/stack-parsers'; +import { describe, expect, it } from 'vitest'; + +import { exceptionFromError } from '../../src/eventbuilder'; +import { defaultStackParser as parser } from '../../src/stack-parsers'; describe('Tracekit - Firefox Tests', () => { it('should parse Firefox 3 error', () => { diff --git a/packages/browser/test/unit/tracekit/ie.test.ts b/packages/browser/test/tracekit/ie.test.ts similarity index 95% rename from packages/browser/test/unit/tracekit/ie.test.ts rename to packages/browser/test/tracekit/ie.test.ts index 53e96b96371f..95d88a6bef34 100644 --- a/packages/browser/test/unit/tracekit/ie.test.ts +++ b/packages/browser/test/tracekit/ie.test.ts @@ -1,6 +1,8 @@ +import { describe, expect, it } from 'vitest'; + import { createStackParser } from '@sentry/utils'; -import { exceptionFromError } from '../../../src/eventbuilder'; -import { chromeStackLineParser, geckoStackLineParser, winjsStackLineParser } from '../../../src/stack-parsers'; +import { exceptionFromError } from '../../src/eventbuilder'; +import { chromeStackLineParser, geckoStackLineParser, winjsStackLineParser } from '../../src/stack-parsers'; const parser = createStackParser(chromeStackLineParser, geckoStackLineParser, winjsStackLineParser); diff --git a/packages/browser/test/unit/tracekit/misc.test.ts b/packages/browser/test/tracekit/misc.test.ts similarity index 96% rename from packages/browser/test/unit/tracekit/misc.test.ts rename to packages/browser/test/tracekit/misc.test.ts index 8cb31f9a7868..7e501b8abeef 100644 --- a/packages/browser/test/unit/tracekit/misc.test.ts +++ b/packages/browser/test/tracekit/misc.test.ts @@ -1,5 +1,7 @@ -import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParser as parser } from '../../../src/stack-parsers'; +import { describe, expect, it } from 'vitest'; + +import { exceptionFromError } from '../../src/eventbuilder'; +import { defaultStackParser as parser } from '../../src/stack-parsers'; describe('Tracekit - Misc Tests', () => { it('should parse PhantomJS 1.19 error', () => { diff --git a/packages/browser/test/unit/tracekit/opera.test.ts b/packages/browser/test/tracekit/opera.test.ts similarity index 98% rename from packages/browser/test/unit/tracekit/opera.test.ts rename to packages/browser/test/tracekit/opera.test.ts index e86855dc172a..352737fba5d0 100644 --- a/packages/browser/test/unit/tracekit/opera.test.ts +++ b/packages/browser/test/tracekit/opera.test.ts @@ -1,7 +1,9 @@ +import { describe, expect, it } from 'vitest'; + import { createStackParser } from '@sentry/utils'; -import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParser, opera10StackLineParser, opera11StackLineParser } from '../../../src/stack-parsers'; +import { exceptionFromError } from '../../src/eventbuilder'; +import { defaultStackParser, opera10StackLineParser, opera11StackLineParser } from '../../src/stack-parsers'; const operaParser = createStackParser(opera10StackLineParser, opera11StackLineParser); const chromiumParser = defaultStackParser; diff --git a/packages/browser/test/unit/tracekit/react-native.test.ts b/packages/browser/test/tracekit/react-native.test.ts similarity index 99% rename from packages/browser/test/unit/tracekit/react-native.test.ts rename to packages/browser/test/tracekit/react-native.test.ts index 9a74e46007b1..c69703fe0c34 100644 --- a/packages/browser/test/unit/tracekit/react-native.test.ts +++ b/packages/browser/test/tracekit/react-native.test.ts @@ -1,5 +1,7 @@ -import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParser as parser } from '../../../src/stack-parsers'; +import { describe, expect, it } from 'vitest'; + +import { exceptionFromError } from '../../src/eventbuilder'; +import { defaultStackParser as parser } from '../../src/stack-parsers'; describe('Tracekit - React Native Tests', () => { it('should parse exceptions for react-native-v8', () => { diff --git a/packages/browser/test/unit/tracekit/react.test.ts b/packages/browser/test/tracekit/react.test.ts similarity index 96% rename from packages/browser/test/unit/tracekit/react.test.ts rename to packages/browser/test/tracekit/react.test.ts index 55ffdc34c537..dd737b798d03 100644 --- a/packages/browser/test/unit/tracekit/react.test.ts +++ b/packages/browser/test/tracekit/react.test.ts @@ -1,5 +1,7 @@ -import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParser as parser } from '../../../src/stack-parsers'; +import { describe, expect, it } from 'vitest'; + +import { exceptionFromError } from '../../src/eventbuilder'; +import { defaultStackParser as parser } from '../../src/stack-parsers'; describe('Tracekit - React Tests', () => { it('should correctly parse Invariant Violation errors and use framesToPop to drop the invariant frame', () => { diff --git a/packages/browser/test/unit/tracekit/safari.test.ts b/packages/browser/test/tracekit/safari.test.ts similarity index 98% rename from packages/browser/test/unit/tracekit/safari.test.ts rename to packages/browser/test/tracekit/safari.test.ts index 470ed1d7b8dc..ca94df924910 100644 --- a/packages/browser/test/unit/tracekit/safari.test.ts +++ b/packages/browser/test/tracekit/safari.test.ts @@ -1,5 +1,7 @@ -import { exceptionFromError } from '../../../src/eventbuilder'; -import { defaultStackParser as parser } from '../../../src/stack-parsers'; +import { describe, expect, it } from 'vitest'; + +import { exceptionFromError } from '../../src/eventbuilder'; +import { defaultStackParser as parser } from '../../src/stack-parsers'; describe('Tracekit - Safari Tests', () => { it('should parse Safari 6 error', () => { diff --git a/packages/browser/test/unit/tracing/backgroundtab.test.ts b/packages/browser/test/tracing/backgroundtab.test.ts similarity index 84% rename from packages/browser/test/unit/tracing/backgroundtab.test.ts rename to packages/browser/test/tracing/backgroundtab.test.ts index 2c998744a723..c939da8b4d03 100644 --- a/packages/browser/test/unit/tracing/backgroundtab.test.ts +++ b/packages/browser/test/tracing/backgroundtab.test.ts @@ -1,3 +1,9 @@ +/** + * @vitest-environment jsdom + */ + +import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + import { getCurrentScope } from '@sentry/core'; import { setCurrentClient } from '@sentry/core'; @@ -8,8 +14,8 @@ const patchedDecoder = (!global.window.TextDecoder && (global.window.TextDecoder import { JSDOM } from 'jsdom'; -import { BrowserClient } from '../../../src/client'; -import { registerBackgroundTabDetection } from '../../../src/tracing/backgroundtab'; +import { BrowserClient } from '../../src/client'; +import { registerBackgroundTabDetection } from '../../src/tracing/backgroundtab'; import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; describe('registerBackgroundTabDetection', () => { @@ -30,7 +36,7 @@ describe('registerBackgroundTabDetection', () => { setCurrentClient(client); client.init(); - global.document.addEventListener = jest.fn((event, callback) => { + global.document.addEventListener = vi.fn((event, callback) => { events[event] = callback; }); }); diff --git a/packages/browser/test/unit/tracing/browserTracingIntegration.test.ts b/packages/browser/test/tracing/browserTracingIntegration.test.ts similarity index 94% rename from packages/browser/test/unit/tracing/browserTracingIntegration.test.ts rename to packages/browser/test/tracing/browserTracingIntegration.test.ts index 86e46fe0ccc8..dbdebe473335 100644 --- a/packages/browser/test/unit/tracing/browserTracingIntegration.test.ts +++ b/packages/browser/test/tracing/browserTracingIntegration.test.ts @@ -1,3 +1,9 @@ +/** + * @vitest-environment jsdom + */ + +import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + import { TextDecoder, TextEncoder } from 'util'; const oldTextEncoder = global.window.TextEncoder; const oldTextDecoder = global.window.TextDecoder; @@ -25,15 +31,14 @@ import { startInactiveSpan, } from '@sentry/core'; import type { Span, StartSpanOptions } from '@sentry/types'; -import { timestampInSeconds } from '@sentry/utils'; import { JSDOM } from 'jsdom'; -import { BrowserClient } from '../../../src/client'; -import { WINDOW } from '../../../src/helpers'; +import { BrowserClient } from '../../src/client'; +import { WINDOW } from '../../src/helpers'; import { browserTracingIntegration, startBrowserTracingNavigationSpan, startBrowserTracingPageLoadSpan, -} from '../../../src/tracing/browserTracingIntegration'; +} from '../../src/tracing/browserTracingIntegration'; import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; // We're setting up JSDom here because the Next.js routing instrumentations requires a few things to be present on pageload: @@ -55,6 +60,11 @@ afterAll(() => { Object.defineProperty(WINDOW, 'history', { value: originalGlobalHistory }); }); +afterEach(() => { + vi.useRealTimers(); + performance.clearMarks(); +}); + describe('browserTracingIntegration', () => { beforeEach(() => { getCurrentScope().clear(); @@ -226,29 +236,6 @@ describe('browserTracingIntegration', () => { }); }); - it("trims pageload transactions to the max duration of the transaction's children", async () => { - const client = new BrowserClient( - getDefaultBrowserClientOptions({ - tracesSampleRate: 1, - integrations: [browserTracingIntegration({ idleTimeout: 10 })], - }), - ); - - setCurrentClient(client); - client.init(); - - const pageloadSpan = getActiveSpan(); - const childSpan = startInactiveSpan({ name: 'pageload-child' }); - const timestamp = timestampInSeconds(); - - childSpan.end(timestamp); - - // Wait for 10ms for idle timeout - await new Promise(resolve => setTimeout(resolve, 10)); - - expect(spanToJSON(pageloadSpan!).timestamp).toBe(timestamp); - }); - describe('startBrowserTracingPageLoadSpan', () => { it('works without integration setup', () => { const client = new BrowserClient( @@ -347,7 +334,7 @@ describe('browserTracingIntegration', () => { }); it('calls before beforeStartSpan', () => { - const mockBeforeStartSpan = jest.fn((options: StartSpanOptions) => options); + const mockBeforeStartSpan = vi.fn((options: StartSpanOptions) => options); const client = new BrowserClient( getDefaultBrowserClientOptions({ @@ -371,7 +358,7 @@ describe('browserTracingIntegration', () => { }); it('uses options overridden with beforeStartSpan', () => { - const mockBeforeStartSpan = jest.fn((options: StartSpanOptions) => ({ + const mockBeforeStartSpan = vi.fn((options: StartSpanOptions) => ({ ...options, op: 'test op', })); @@ -414,7 +401,7 @@ describe('browserTracingIntegration', () => { }); it('sets source to "custom" if name is changed in beforeStartSpan', () => { - const mockBeforeStartSpan = jest.fn((options: StartSpanOptions) => ({ + const mockBeforeStartSpan = vi.fn((options: StartSpanOptions) => ({ ...options, name: 'changed', })); @@ -540,7 +527,7 @@ describe('browserTracingIntegration', () => { }); it('calls before beforeStartSpan', () => { - const mockBeforeStartSpan = jest.fn((options: StartSpanOptions) => options); + const mockBeforeStartSpan = vi.fn((options: StartSpanOptions) => options); const client = new BrowserClient( getDefaultBrowserClientOptions({ @@ -568,7 +555,7 @@ describe('browserTracingIntegration', () => { }); it('uses options overridden with beforeStartSpan', () => { - const mockBeforeStartSpan = jest.fn((options: StartSpanOptions) => ({ + const mockBeforeStartSpan = vi.fn((options: StartSpanOptions) => ({ ...options, op: 'test op', })); @@ -596,7 +583,7 @@ describe('browserTracingIntegration', () => { }); it('sets source to "custom" if name is changed in beforeStartSpan', () => { - const mockBeforeStartSpan = jest.fn((options: StartSpanOptions) => ({ + const mockBeforeStartSpan = vi.fn((options: StartSpanOptions) => ({ ...options, name: 'changed', })); @@ -929,7 +916,7 @@ describe('browserTracingIntegration', () => { describe('idleTimeout', () => { it('is created by default', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const client = new BrowserClient( getDefaultBrowserClientOptions({ tracesSampleRate: 1, @@ -955,15 +942,16 @@ describe('browserTracingIntegration', () => { // inner1 is now ended, all good expect(spans).toHaveLength(1); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); // idle span itself is now ended - expect(spans).toHaveLength(2); - expect(spans[1]).toBe(idleSpan); + // there is also the `sentry-tracing-init` span included + expect(spans).toHaveLength(3); + expect(spans[2]).toBe(idleSpan); }); it('can be a custom value', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const client = new BrowserClient( getDefaultBrowserClientOptions({ @@ -990,18 +978,19 @@ describe('browserTracingIntegration', () => { // inner1 is now ended, all good expect(spans).toHaveLength(1); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); // idle span itself is now ended - expect(spans).toHaveLength(2); - expect(spans[1]).toBe(idleSpan); + // there is also the `sentry-tracing-init` span included + expect(spans).toHaveLength(3); + expect(spans[2]).toBe(idleSpan); }); }); // TODO(lforst): I cannot manage to get this test to pass. /* it('heartbeatInterval can be a custom value', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const interval = 200; @@ -1015,7 +1004,7 @@ describe('browserTracingIntegration', () => { setCurrentClient(client); client.init(); - const mockFinish = jest.fn(); + const mockFinish = vi.fn(); // eslint-disable-next-line deprecation/deprecation const transaction = getActiveTransaction() as IdleTransaction; transaction.sendAutoFinishSignal(); @@ -1025,7 +1014,7 @@ describe('browserTracingIntegration', () => { span!.end(); // activities = 0 expect(mockFinish).toHaveBeenCalledTimes(0); - jest.advanceTimersByTime(interval * 3); + vi.advanceTimersByTime(interval * 3); expect(mockFinish).toHaveBeenCalledTimes(1); }); */ diff --git a/packages/browser/test/unit/tracing/request.test.ts b/packages/browser/test/tracing/request.test.ts similarity index 95% rename from packages/browser/test/unit/tracing/request.test.ts rename to packages/browser/test/tracing/request.test.ts index 384d62bf52e2..f1067c9e4b52 100644 --- a/packages/browser/test/unit/tracing/request.test.ts +++ b/packages/browser/test/tracing/request.test.ts @@ -1,13 +1,15 @@ +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; + import * as browserUtils from '@sentry-internal/browser-utils'; import type { Client } from '@sentry/types'; import * as utils from '@sentry/utils'; -import { WINDOW } from '../../../src/helpers'; +import { WINDOW } from '../../src/helpers'; -import { extractNetworkProtocol, instrumentOutgoingRequests, shouldAttachHeaders } from '../../../src/tracing/request'; +import { extractNetworkProtocol, instrumentOutgoingRequests, shouldAttachHeaders } from '../../src/tracing/request'; beforeAll(() => { - // @ts-expect-error need to override global Request because it's not in the jest environment (even with an - // `@jest-environment jsdom` directive, for some reason) + // @ts-expect-error need to override global Request because it's not in the vi environment (even with an + // `@vi-environment jsdom` directive, for some reason) global.Request = {}; }); @@ -15,7 +17,7 @@ class MockClient implements Partial { public addEventProcessor: () => void; constructor() { // Mock addEventProcessor function - this.addEventProcessor = jest.fn(); + this.addEventProcessor = vi.fn(); } } @@ -23,13 +25,13 @@ describe('instrumentOutgoingRequests', () => { let client: Client; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); client = new MockClient() as unknown as Client; }); it('instruments fetch and xhr requests', () => { - const addFetchSpy = jest.spyOn(utils, 'addFetchInstrumentationHandler'); - const addXhrSpy = jest.spyOn(browserUtils, 'addXhrInstrumentationHandler'); + const addFetchSpy = vi.spyOn(utils, 'addFetchInstrumentationHandler'); + const addXhrSpy = vi.spyOn(browserUtils, 'addXhrInstrumentationHandler'); instrumentOutgoingRequests(client); @@ -38,7 +40,7 @@ describe('instrumentOutgoingRequests', () => { }); it('does not instrument fetch requests if traceFetch is false', () => { - const addFetchSpy = jest.spyOn(utils, 'addFetchInstrumentationHandler'); + const addFetchSpy = vi.spyOn(utils, 'addFetchInstrumentationHandler'); instrumentOutgoingRequests(client, { traceFetch: false }); @@ -46,7 +48,7 @@ describe('instrumentOutgoingRequests', () => { }); it('does not instrument xhr requests if traceXHR is false', () => { - const addXhrSpy = jest.spyOn(browserUtils, 'addXhrInstrumentationHandler'); + const addXhrSpy = vi.spyOn(browserUtils, 'addXhrInstrumentationHandler'); instrumentOutgoingRequests(client, { traceXHR: false }); @@ -60,7 +62,7 @@ interface ProtocolInfo { } describe('HTTPTimings', () => { - describe('Extracting version from ALPN protocol', () => { + test('Extracting version from ALPN protocol', () => { const nextHopToNetworkVersion: Record = { 'http/0.9': { name: 'http', version: '0.9' }, 'http/1.0': { name: 'http', version: '1.0' }, diff --git a/packages/browser/test/unit/transports/fetch.test.ts b/packages/browser/test/transports/fetch.test.ts similarity index 90% rename from packages/browser/test/unit/transports/fetch.test.ts rename to packages/browser/test/transports/fetch.test.ts index ae8a5d43a6e0..ccba3282251c 100644 --- a/packages/browser/test/unit/transports/fetch.test.ts +++ b/packages/browser/test/transports/fetch.test.ts @@ -1,8 +1,11 @@ +import type { Mock } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; + import type { EventEnvelope, EventItem } from '@sentry/types'; import { createEnvelope, serializeEnvelope } from '@sentry/utils'; -import { makeFetchTransport } from '../../../src/transports/fetch'; -import type { BrowserTransportOptions } from '../../../src/transports/types'; +import { makeFetchTransport } from '../../src/transports/fetch'; +import type { BrowserTransportOptions } from '../../src/transports/types'; const DEFAULT_FETCH_TRANSPORT_OPTIONS: BrowserTransportOptions = { url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7', @@ -30,7 +33,7 @@ class Headers { describe('NewFetchTransport', () => { it('calls fetch with the given URL', async () => { - const mockFetch = jest.fn(() => + const mockFetch = vi.fn(() => Promise.resolve({ headers: new Headers(), status: 200, @@ -53,10 +56,10 @@ describe('NewFetchTransport', () => { it('sets rate limit headers', async () => { const headers = { - get: jest.fn(), + get: vi.fn(), }; - const mockFetch = jest.fn(() => + const mockFetch = vi.fn(() => Promise.resolve({ headers, status: 200, @@ -74,7 +77,7 @@ describe('NewFetchTransport', () => { }); it('allows for custom options to be passed in', async () => { - const mockFetch = jest.fn(() => + const mockFetch = vi.fn(() => Promise.resolve({ headers: new Headers(), status: 200, @@ -102,7 +105,7 @@ describe('NewFetchTransport', () => { }); it('handles when `getNativetypeof window.fetchementation` is undefined', async () => { - const mockFetch = jest.fn(() => undefined) as unknown as typeof window.fetch; + const mockFetch = vi.fn(() => undefined) as unknown as typeof window.fetch; const transport = makeFetchTransport(DEFAULT_FETCH_TRANSPORT_OPTIONS, mockFetch); expect(mockFetch).toHaveBeenCalledTimes(0); @@ -111,7 +114,7 @@ describe('NewFetchTransport', () => { }); it('correctly sets keepalive flag', async () => { - const mockFetch = jest.fn(() => + const mockFetch = vi.fn(() => Promise.resolve({ headers: new Headers(), status: 200, @@ -143,7 +146,7 @@ describe('NewFetchTransport', () => { expect(mockFetch).toHaveBeenNthCalledWith(i, expect.any(String), expect.objectContaining({ keepalive })); } - (mockFetch as jest.Mock).mockClear(); + (mockFetch as Mock).mockClear(); // Limit resets when requests have resolved // Now try based on # of pending requests diff --git a/packages/browser/test/unit/transports/offline.test.ts b/packages/browser/test/transports/offline.test.ts similarity index 96% rename from packages/browser/test/unit/transports/offline.test.ts rename to packages/browser/test/transports/offline.test.ts index ed4cd770101d..a8352cb4102d 100644 --- a/packages/browser/test/unit/transports/offline.test.ts +++ b/packages/browser/test/transports/offline.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it } from 'vitest'; + import 'fake-indexeddb/auto'; import { TextDecoder, TextEncoder } from 'util'; @@ -10,8 +12,9 @@ import type { } from '@sentry/types'; import { createEnvelope } from '@sentry/utils'; -import { MIN_DELAY } from '../../../../core/src/transports/offline'; -import { createStore, makeBrowserOfflineTransport, push, shift, unshift } from '../../../src/transports/offline'; +import { createStore, makeBrowserOfflineTransport, push, shift, unshift } from '../../src/transports/offline'; + +const MIN_DELAY = 100; function deleteDatabase(name: string): Promise { return new Promise((resolve, reject) => { diff --git a/packages/browser/test/unit/userfeedback.test.ts b/packages/browser/test/userfeedback.test.ts similarity index 92% rename from packages/browser/test/unit/userfeedback.test.ts rename to packages/browser/test/userfeedback.test.ts index cb498cac7893..93a8de33690b 100644 --- a/packages/browser/test/unit/userfeedback.test.ts +++ b/packages/browser/test/userfeedback.test.ts @@ -1,4 +1,6 @@ -import { createUserFeedbackEnvelope } from '../../src/userfeedback'; +import { describe, expect, test } from 'vitest'; + +import { createUserFeedbackEnvelope } from '../src/userfeedback'; describe('userFeedback', () => { test('creates user feedback envelope header', () => { diff --git a/packages/browser/test/unit/utils/lazyLoadIntegration.test.ts b/packages/browser/test/utils/lazyLoadIntegration.test.ts similarity index 92% rename from packages/browser/test/unit/utils/lazyLoadIntegration.test.ts rename to packages/browser/test/utils/lazyLoadIntegration.test.ts index fa6bc62fb4fd..ec88ae49a1a9 100644 --- a/packages/browser/test/unit/utils/lazyLoadIntegration.test.ts +++ b/packages/browser/test/utils/lazyLoadIntegration.test.ts @@ -1,6 +1,12 @@ +/** + * @vitest-environment jsdom + */ + +import { afterAll, afterEach, beforeEach, describe, expect, test } from 'vitest'; + import { TextDecoder, TextEncoder } from 'util'; -import { SDK_VERSION, lazyLoadIntegration } from '../../../src'; -import * as Sentry from '../../../src'; +import { SDK_VERSION, lazyLoadIntegration } from '../../src'; +import * as Sentry from '../../src'; const patchedEncoder = (!global.window.TextEncoder && (global.window.TextEncoder = TextEncoder)) || true; // @ts-expect-error patch the encoder on the window, else importing JSDOM fails (deleted in afterAll) const patchedDecoder = (!global.window.TextDecoder && (global.window.TextDecoder = TextDecoder)) || true; diff --git a/packages/browser/tsconfig.test.json b/packages/browser/tsconfig.test.json index 9bdd2aa76dab..00cada2d8bcf 100644 --- a/packages/browser/tsconfig.test.json +++ b/packages/browser/tsconfig.test.json @@ -1,13 +1,9 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*"], - "exclude": ["test/integration/**/*"], + "include": ["test/**/*", "vite.config.ts"], "compilerOptions": { - // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] - // other package-specific, test-specific options } } diff --git a/packages/browser/vite.config.ts b/packages/browser/vite.config.ts new file mode 100644 index 000000000000..a5523c61f601 --- /dev/null +++ b/packages/browser/vite.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +import baseConfig from '../../vite/vite.config'; + +export default defineConfig({ + ...baseConfig, + test: { + ...baseConfig.test, + }, +});