Skip to content

Commit

Permalink
chore: added bugsnag plugin for backward compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
MoumitaM committed Jul 17, 2024
1 parent fd1e424 commit 55a7a9f
Show file tree
Hide file tree
Showing 28 changed files with 1,394 additions and 61 deletions.
57 changes: 56 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/analytics-js-common/src/types/ErrorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import type { IPluginEngine } from './PluginEngine';
import type { ILogger } from './Logger';
import type { BufferQueue } from '../services/BufferQueue/BufferQueue';
import type { IHttpClient } from './HttpClient';
import type { IExternalSrcLoader } from '../services/ExternalSrcLoader/types';

export type SDKError = unknown | Error | ErrorEvent | Event | PromiseRejectionEvent;

export interface IErrorHandler {
logger?: ILogger;
pluginEngine?: IPluginEngine;
errorBuffer: BufferQueue<PreLoadErrorData>;
init(httpClient?: IHttpClient): void;
init(httpClient: IHttpClient, externalSrcLoader: IExternalSrcLoader): void;
onError(
error: SDKError,
context?: string,
Expand Down
1 change: 1 addition & 0 deletions packages/analytics-js-common/src/types/PluginsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface IPluginsManager {

export type PluginName =
| 'BeaconQueue'
| 'Bugsnag'
| 'CustomConsentManager'
| 'DeviceModeDestinations'
| 'DeviceModeTransformation'
Expand Down
2 changes: 1 addition & 1 deletion packages/analytics-js-plugins/.size-limit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export default [
{
name: 'Remote Module Federated Plugins - CDN',
path: 'dist/cdn/modern/plugins/rsa-plugins-*.js',
limit: '7.5 KiB',
limit: '8.5 KiB',
},
];
5 changes: 5 additions & 0 deletions packages/analytics-js-plugins/__fixtures__/msw.handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ const handlers = [
status: 500,
});
}),
http.get(`https://asdf.com/bugsnag.min.js`, () => {
return new HttpResponse(errorMessage, {
status: 404,
});
}),
];

export { handlers };
110 changes: 110 additions & 0 deletions packages/analytics-js-plugins/__tests__/bugsnag/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { signal } from '@preact/signals-core';
import { clone } from 'ramda';
import { Bugsnag } from '../../src/bugsnag';
import * as bugsnagConstants from '../../src/bugsnag/constants';

describe('Plugin - Bugsnag', () => {
const originalState = {
plugins: {
loadedPlugins: signal([]),
},
lifecycle: {
writeKey: signal('dummy-write-key'),
},
source: signal({
id: 'test-source-id',
}),
context: {
app: signal({
name: 'test-app',
namespace: 'test-namespace',
version: '1.0.0',
installType: 'npm',
}),
},
};

let state: any;

const origApiKey = bugsnagConstants.API_KEY;
const origMaxSDKWait = bugsnagConstants.MAX_WAIT_FOR_SDK_LOAD_MS;

const mountBugsnagSDK = () => {
(window as any).bugsnag = jest.fn(() => ({
notifier: { version: '6.0.0' },
leaveBreadcrumb: jest.fn(),
notify: jest.fn(),
}));
return (window as any).bugsnag();
};

beforeEach(() => {
state = clone(originalState);
delete (window as any).bugsnag;
bugsnagConstants.API_KEY = origApiKey;
bugsnagConstants.MAX_WAIT_FOR_SDK_LOAD_MS = origMaxSDKWait;
});

it('should add Bugsnag plugin in the loaded plugin list', () => {
Bugsnag().initialize(state);
expect(state.plugins.loadedPlugins.value.includes('Bugsnag')).toBe(true);
});

it('should reject the promise if the Api Key is not valid', async () => {
bugsnagConstants.API_KEY = '{{ dummy api key }}';

const pluginInitPromise = Bugsnag().errorReportingProvider.init();

await expect(pluginInitPromise).rejects.toThrow(
'The Bugsnag API key ({{ dummy api key }}) is invalid or not provided.',
);
});

it('should reject the promise if the Bugsnag client could not be initialized', async () => {
bugsnagConstants.MAX_WAIT_FOR_SDK_LOAD_MS = 1000;

const mockExtSrcLoader = {
loadJSFile: jest.fn(() => Promise.resolve()),
};

const pluginInitPromise = Bugsnag().errorReportingProvider.init(state, mockExtSrcLoader);

await expect(pluginInitPromise).rejects.toThrow(
'A timeout 1000 ms occurred while trying to load the Bugsnag SDK.',
);
});

it('should initialize the Bugsnag SDK and return the client instance', async () => {
setTimeout(() => {
mountBugsnagSDK();
}, 500);

const mockExtSrcLoader = {
loadJSFile: jest.fn(() => Promise.resolve()),
};

const pluginInitPromise = Bugsnag().errorReportingProvider.init(state, mockExtSrcLoader);

await expect(pluginInitPromise).resolves.toBeDefined();
});

it('should notify the client', () => {
const bsClient = mountBugsnagSDK();

const mockError = new Error('Test Error');

Bugsnag().errorReportingProvider.notify(bsClient, mockError);

expect(bsClient.notify).toHaveBeenCalledWith(mockError);
});

it('should leave a breadcrumb', () => {
const bsClient = mountBugsnagSDK();

const mockMessage = 'Test Breadcrumb';

Bugsnag().errorReportingProvider.breadcrumb(bsClient, mockMessage);

expect(bsClient.leaveBreadcrumb).toHaveBeenCalledWith(mockMessage);
});
});
Loading

0 comments on commit 55a7a9f

Please sign in to comment.