@grafana/faro-core
Version:
Core package of Faro.
296 lines • 15.9 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var initialize_1 = require("../../initialize");
var testUtils_1 = require("../../testUtils");
var transports_1 = require("../../transports");
var apiTestHelpers_1 = require("../apiTestHelpers");
var userAction_1 = __importDefault(require("../userActions/userAction"));
var initialize_2 = require("./initialize");
describe('api.exceptions', function () {
function createAPI(_a) {
var _b = _a === void 0 ? { dedupe: true } : _a, dedupe = _b.dedupe;
var transport = new testUtils_1.MockTransport();
var config = (0, testUtils_1.mockConfig)({
dedupe: dedupe,
transports: [transport],
});
var api = (0, initialize_1.initializeFaro)(config).api;
return [api, transport];
}
describe('pushError', function () {
var api;
var transport;
beforeEach(function () {
var _a;
_a = createAPI(), api = _a[0], transport = _a[1];
});
it('error with overrides', function () {
var frames = [
{
filename: 'foo.js',
function: 'FooFn',
colno: 4,
lineno: 23,
},
{
filename: 'bar.js',
function: 'BarFn',
colno: 6,
lineno: 52,
},
];
var additionalContext = {
message: 'React error boundary',
componentStackTrace: 'componentStackTrace',
};
api.pushError(new Error('test exception'), {
stackFrames: frames,
type: 'TestError',
context: additionalContext,
});
expect(transport.items).toHaveLength(1);
var payload = transport.items[0];
expect(payload === null || payload === void 0 ? void 0 : payload.payload).toBeTruthy();
expect(payload === null || payload === void 0 ? void 0 : payload.type).toEqual(transports_1.TransportItemType.EXCEPTION);
var evt = payload === null || payload === void 0 ? void 0 : payload.payload;
expect(evt.type).toEqual('TestError');
expect(evt.value).toEqual('test exception');
expect(evt.stacktrace).toEqual({ frames: frames });
expect(evt.context).toEqual(additionalContext);
});
it('error without overrides', function () {
var _a, _b;
var err = new Error('test');
api.pushError(err);
expect(transport.items).toHaveLength(1);
var payload = transport.items[0];
expect((_a = payload === null || payload === void 0 ? void 0 : payload.meta.app) === null || _a === void 0 ? void 0 : _a.name).toEqual('test');
expect(payload === null || payload === void 0 ? void 0 : payload.payload).toBeTruthy();
expect(payload === null || payload === void 0 ? void 0 : payload.type).toEqual(transports_1.TransportItemType.EXCEPTION);
var evt = payload === null || payload === void 0 ? void 0 : payload.payload;
expect(evt.type).toEqual('Error');
expect(evt.value).toEqual('test');
expect(evt.timestamp).toBeTruthy();
var stacktrace = evt.stacktrace;
expect(stacktrace).toBeTruthy();
expect(stacktrace === null || stacktrace === void 0 ? void 0 : stacktrace.frames.length).toBeGreaterThan(3);
expect((_b = stacktrace === null || stacktrace === void 0 ? void 0 : stacktrace.frames[0]) === null || _b === void 0 ? void 0 : _b.filename).toEqual('Error: test');
});
it('does not stringify empty context', function () {
api.pushError(new Error('test'));
api.pushError(new Error('test2'), {
context: {},
});
expect(transport.items).toHaveLength(2);
expect(transport.items[0].payload.context).toBeUndefined();
expect(transport.items[0].payload.context).toBeUndefined();
});
it('add the original error to the payload', function () {
var _a;
var transport = new testUtils_1.MockTransport();
var config = (0, testUtils_1.mockConfig)({
transports: [transport],
preserveOriginalError: true,
});
var api = (0, initialize_1.initializeFaro)(config).api;
var error = new Error('test');
api.pushError(error, { originalError: error });
expect(transport.items).toHaveLength(1);
expect(((_a = transport.items[0]) === null || _a === void 0 ? void 0 : _a.payload).originalError).toEqual(error);
});
describe('Filtering', function () {
it('filters the same event', function () {
var error = new Error('test');
api.pushError(error);
expect(transport.items).toHaveLength(1);
api.pushError(error);
expect(transport.items).toHaveLength(1);
});
it("doesn't filter events with same message and different stacktrace", function () {
var error1 = new Error('test');
var error2 = new Error('test');
api.pushError(error1);
expect(transport.items).toHaveLength(1);
api.pushError(error2);
expect(transport.items).toHaveLength(2);
});
it("doesn't filter events with other message and same stacktrace", function () {
var error = new Error('test');
api.pushError(error);
expect(transport.items).toHaveLength(1);
error.message = 'test2';
api.pushError(error);
expect(transport.items).toHaveLength(2);
});
it("doesn't filter events with same message and same stacktrace but different type", function () {
var error = new Error('test');
api.pushError(error);
expect(transport.items).toHaveLength(1);
error.name = 'Another Type';
api.pushError(error);
expect(transport.items).toHaveLength(2);
});
it("filters an event and doesn't filter the next different one", function () {
var error = new Error('test');
api.pushError(error);
expect(transport.items).toHaveLength(1);
api.pushError(error);
expect(transport.items).toHaveLength(1);
error.name = 'Another Type';
api.pushError(error);
expect(transport.items).toHaveLength(2);
});
it("doesn't filter when dedupe is false", function () {
var _a;
_a = createAPI({ dedupe: false }), api = _a[0], transport = _a[1];
var error = new Error('test');
api.pushError(error);
expect(transport.items).toHaveLength(1);
api.pushError(error);
expect(transport.items).toHaveLength(2);
});
it("doesn't filter when skipDedupe is true", function () {
var error = new Error('test');
api.pushError(error);
expect(transport.items).toHaveLength(1);
api.pushError(error, {
skipDedupe: true,
});
expect(transport.items).toHaveLength(2);
});
it("doesn't filter events with same message, same stacktrace, same type but different context", function () {
var error = new Error('test');
api.pushError(error, { context: { foo: 'bar' } });
expect(transport.items).toHaveLength(1);
api.pushError(error, { context: { bar: 'baz' } });
expect(transport.items).toHaveLength(2);
});
it('uses traceId and spanId from custom context', function () {
var _a;
var spanContext = {
traceId: 'my-trace-id',
spanId: 'my-span-id',
};
var error = new Error('test');
api.pushError(error, { spanContext: spanContext });
expect(transport.items).toHaveLength(1);
expect(((_a = transport.items[0]) === null || _a === void 0 ? void 0 : _a.payload).trace).toStrictEqual({
trace_id: 'my-trace-id',
span_id: 'my-span-id',
});
});
it('Sets the timestamp to the provided custom timestamp', function () {
var _a;
api.pushEvent('test', undefined, undefined, { timestampOverwriteMs: 123 });
expect(transport.items).toHaveLength(1);
expect(((_a = transport.items[0]) === null || _a === void 0 ? void 0 : _a.payload).timestamp).toBe('1970-01-01T00:00:00.123Z');
});
it('Adds error cause to error context', function () {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
var error = new Error('test', { cause: 'foo' });
var error2 = new Error('test2', { cause: [1, 3] });
var error3 = new Error('test3', { cause: { a: 'b' } });
var error4 = new Error('test4', { cause: new Error('original error') });
var error5 = new Error('test5', { cause: null });
var error6 = new Error('test6', { cause: undefined });
var error7 = new Error('test6');
api.pushError(error);
api.pushError(error2);
api.pushError(error3);
api.pushError(error4);
api.pushError(error5);
api.pushError(error6);
api.pushError(error7);
expect(transport.items).toHaveLength(7);
expect((_b = (_a = transport.items[0]) === null || _a === void 0 ? void 0 : _a.payload) === null || _b === void 0 ? void 0 : _b.context).toEqual({ cause: 'foo' });
expect((_d = (_c = transport.items[1]) === null || _c === void 0 ? void 0 : _c.payload) === null || _d === void 0 ? void 0 : _d.context).toEqual({ cause: '[1,3]' });
expect((_f = (_e = transport.items[2]) === null || _e === void 0 ? void 0 : _e.payload) === null || _f === void 0 ? void 0 : _f.context).toEqual({ cause: '{"a":"b"}' });
expect((_h = (_g = transport.items[3]) === null || _g === void 0 ? void 0 : _g.payload) === null || _h === void 0 ? void 0 : _h.context).toEqual({ cause: 'Error: original error' });
expect((_k = (_j = transport.items[4]) === null || _j === void 0 ? void 0 : _j.payload) === null || _k === void 0 ? void 0 : _k.context).toBeUndefined();
expect((_m = (_l = transport.items[5]) === null || _l === void 0 ? void 0 : _l.payload) === null || _m === void 0 ? void 0 : _m.context).toBeUndefined();
expect((_p = (_o = transport.items[6]) === null || _o === void 0 ? void 0 : _o.payload) === null || _p === void 0 ? void 0 : _p.context).toBeUndefined();
});
it('stringifies all values added to the context', function () {
var _a, _b;
api.pushError(new Error('Error with context'), {
context: {
// @ts-expect-error
a: 1,
b: 'foo',
// @ts-expect-error
c: true,
// @ts-expect-error
d: { e: 'bar' },
// @ts-expect-error
g: null,
// @ts-expect-error
h: undefined,
// @ts-expect-error
i: [1, 2, 3],
},
});
var context = (_b = (_a = transport.items[0]) === null || _a === void 0 ? void 0 : _a.payload) === null || _b === void 0 ? void 0 : _b.context;
expect(context).toStrictEqual({
a: '1',
b: 'foo',
c: 'true',
d: '{"e":"bar"}',
g: 'null',
h: 'undefined',
i: '[1,2,3]',
});
Object.values(context !== null && context !== void 0 ? context : {}).forEach(function (value) {
expect(typeof value).toBe('string');
});
});
});
describe('config.ignoreErrors', function () {
it('will filter out errors by string or regex', function () {
var _a;
var transport = new testUtils_1.MockTransport();
var api = (0, initialize_1.initializeFaro)((0, testUtils_1.mockConfig)({
transports: [transport],
ignoreErrors: ['Error: ResizeObserver', /FetchError[:\s\w\/]*pwc/, 'chrome-extension://mock-extension-id'],
})).api;
api.pushError(new Error('Error: ResizeObserver loop limit exceeded'));
api.pushError(new Error('FetchError: 404 \n Instantiating https://pwc.grafana.net/public/react-router-dom'));
api.pushError(new Error('FetchError: 404 \n Instantiating https://pwc.grafana.net/public/@emotion/css'));
var typeErrorMsg = 'TypeError: _.viz is undefined';
api.pushError(new Error(typeErrorMsg));
var mockErrorWithStacktrace = new Error('Mock error for testing');
mockErrorWithStacktrace.name = 'MockError';
mockErrorWithStacktrace.stack = "MockError: Mock error for testing\n at mockFunction (chrome-extension://mock-extension-id/mock-file.js:10:15)\n at anotherFunction (chrome-extension://mock-extension-id/mock-file.js:20:5)\n at Object.<anonymous> (chrome-extension://mock-extension-id/mock-file.js:30:3)";
api.pushError(mockErrorWithStacktrace);
expect(transport.items).toHaveLength(1);
expect(((_a = transport.items[0]) === null || _a === void 0 ? void 0 : _a.payload).value).toEqual(typeErrorMsg);
});
});
});
describe('User action', function () {
it('buffers the error if a user action is in progress', function () {
var internalLogger = testUtils_1.mockInternalLogger;
var config = (0, testUtils_1.mockConfig)();
var api = (0, initialize_2.initializeExceptionsAPI)({
unpatchedConsole: console,
internalLogger: internalLogger,
config: config,
metas: apiTestHelpers_1.mockMetas,
transports: apiTestHelpers_1.mockTransports,
tracesApi: apiTestHelpers_1.mockTracesApi,
userActionsApi: apiTestHelpers_1.mockUserActionsApi,
});
apiTestHelpers_1.mockUserActionsApi.getActiveUserAction.mockReturnValueOnce(new userAction_1.default({
name: 'test',
trigger: 'foo',
transports: apiTestHelpers_1.mockTransports,
pushEvent: jest.fn(),
}));
api.pushError(new Error('test'));
expect(apiTestHelpers_1.mockTransports.execute).not.toHaveBeenCalled();
});
});
});
//# sourceMappingURL=initialize.test.js.map