fyipe-staging
Version:
Fyipe is a JS package that tracks error event and send logs from your applications to your fyipe dashboard.
489 lines (476 loc) • 19.3 kB
JavaScript
const chai = require('chai');
const expect = chai.expect;
chai.use(require('chai-http'));
import { user, generateSecondRandomBusinessEmail } from './util';
const API_URL = 'http://localhost:3002/api';
const request = chai.request.agent(API_URL);
const timeout = 5000;
import FyipeTracker from '../src/tracker';
const customTimeline = {
category: 'cart',
content: {
message: 'test-content',
},
type: 'info',
};
let errorTracker;
describe('Tracker Timeline', function() {
const sleep = milliseconds => {
return new Promise(resolve => setTimeout(resolve, milliseconds));
};
this.timeout(timeout + 1000);
let projectId, token, componentId;
// create a new user
const component = { name: 'Our Component' };
before(function(done) {
this.timeout(60000);
sleep(5000).then(() => {
user.email = generateSecondRandomBusinessEmail();
request
.post('/user/signup')
.send(user)
.end(function(err, res) {
const project = res.body.project;
projectId = project._id;
token = res.body.tokens.jwtAccessToken;
request
.post(`/component/${projectId}`)
.set('Authorization', `Basic ${token}`)
.send(component)
.end(function(err, res) {
componentId = res.body._id;
request
.post(
`/error-tracker/${projectId}/${componentId}/create`
)
.set('Authorization', `Basic ${token}`)
.send({ name: 'Application FyipeTracker' })
.end(function(err, res) {
expect(res).to.have.status(200);
expect(res.body).to.be.an('object');
expect(res.body).to.have.property('_id');
errorTracker = res.body;
done();
});
});
});
});
});
it('should take in custom timeline event', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
tracker.addToTimeline(
customTimeline.category,
customTimeline.content,
customTimeline.type
);
const timeline = tracker.getTimeline();
expect(timeline).to.be.an('array');
expect(timeline).to.have.lengthOf(1);
expect(timeline[0].category).to.equal(customTimeline.category);
});
it('should ensure timeline event contains eventId and timestamp', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
tracker.addToTimeline(
customTimeline.category,
customTimeline.content,
customTimeline.type
);
const timeline = tracker.getTimeline();
expect(timeline[0].eventId).to.be.a('string');
expect(timeline[0].timestamp).to.be.a('number');
});
it('should ensure different timeline event have the same eventId', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
tracker.addToTimeline(
customTimeline.category,
customTimeline.content,
customTimeline.type
);
tracker.addToTimeline(
customTimeline.category,
customTimeline.content,
'error'
);
const timeline = tracker.getTimeline();
expect(timeline.length).to.equal(2); // two timeline events
expect(timeline[0].eventId).to.equal(timeline[1].eventId); // their eveentId is the same till there is an error sent to the server
});
it('should ensure max timline cant be set as a negative number', function() {
const options = { maxTimeline: -5 };
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key,
options
);
tracker.addToTimeline(
customTimeline.category,
customTimeline.content,
customTimeline.type
);
tracker.addToTimeline(
customTimeline.category,
customTimeline.content,
'error'
);
const timeline = tracker.getTimeline();
expect(timeline.length).to.equal(2); // two timeline events
});
it('should ensure new timeline event after max timeline are discarded', function() {
const options = { maxTimeline: 2 };
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key,
options
);
const customTimeline2 = {
category: 'logout',
content: {
message: 'test-content',
},
type: 'success',
};
// add 3 timelinee events
tracker.addToTimeline(
customTimeline.category,
customTimeline.content,
customTimeline.type
);
tracker.addToTimeline(
customTimeline2.category,
customTimeline2.content,
customTimeline2.type
);
tracker.addToTimeline(
customTimeline.category,
customTimeline.content,
'debug'
);
const timeline = tracker.getTimeline();
expect(timeline.length).to.equal(options.maxTimeline);
expect(timeline[0].type).to.equal(customTimeline.type);
expect(timeline[1].category).to.equal(customTimeline2.category);
});
});
describe('Tags', function() {
it('should add tags ', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
const tag = { key: 'location', value: 'Atlanta' };
tracker.setTag(tag.key, tag.value);
const availableTags = tracker._getTags();
expect(availableTags).to.be.an('array');
expect(availableTags.length).to.equal(1);
expect(availableTags[0]).to.have.property('key');
expect(availableTags[0]).to.have.property('value');
});
it('should add multiple tags ', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
const tags = [
{ key: 'location', value: 'Atlanta' },
{ key: 'city', value: 'anywhere' },
{ key: 'device', value: 'iOS' },
];
tracker.setTags(tags);
const availableTags = tracker._getTags();
expect(availableTags).to.be.an('array');
expect(availableTags.length).to.equal(3);
});
it('should overwrite existing keys to avoid duplicate tags ', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
const tags = [
{ key: 'location', value: 'Atlanta' },
{ key: 'city', value: 'anywhere' },
{ key: 'location', value: 'Paris' },
{ key: 'device', value: 'iOS' },
{ key: 'location', value: 'London' },
];
tracker.setTags(tags);
const availableTags = tracker._getTags();
expect(availableTags).to.be.an('array');
expect(availableTags.length).to.equal(3); // since location repeated itself multiple times
expect(availableTags[0].key).to.be.equal('location');
expect(availableTags[0].value).to.be.equal('London'); // latest value for that tag
});
});
describe('Fingerpint', function() {
it('should create fingerprint as message for error capture without any fingerprint', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
const errorMessage = 'Uncaught Exception';
tracker.captureMessage(errorMessage);
const event = tracker.getCurrentEvent();
expect(event.fingerprint[0]).to.equal(errorMessage);
});
it('should use defined fingerprint array for error capture with fingerprint', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
const fingerprints = ['custom', 'errors'];
tracker.setFingerprint(fingerprints);
const errorMessage = 'Uncaught Exception';
tracker.captureMessage(errorMessage);
const event = tracker.getCurrentEvent();
expect(event.fingerprint[0]).to.equal(fingerprints[0]);
expect(event.fingerprint[1]).to.equal(fingerprints[1]);
});
it('should use defined fingerprint string for error capture with fingerprint', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
const fingerprint = 'custom-fingerprint';
tracker.setFingerprint(fingerprint);
const errorMessage = 'Uncaught Exception';
tracker.captureMessage(errorMessage);
const event = tracker.getCurrentEvent();
expect(event.fingerprint[0]).to.equal(fingerprint);
});
});
describe('Capture Message', function() {
it('should create an event ready for the server', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
const errorMessage = 'This is a test';
tracker.captureMessage(errorMessage);
const event = tracker.getCurrentEvent();
expect(event.type).to.equal('message');
expect(event.exception.message).to.equal(errorMessage);
});
it('should create an event ready for the server while having the timeline with same event id', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
tracker.addToTimeline(
customTimeline.category,
customTimeline.content,
customTimeline.type
);
const errorMessage = 'This is a test';
tracker.captureMessage(errorMessage);
const event = tracker.getCurrentEvent();
expect(event.timeline.length).to.equal(2);
expect(event.eventId).to.equal(event.timeline[0].eventId);
expect(event.exception.message).to.equal(errorMessage);
});
});
describe('Capture Exception', function() {
it('should create an event ready for the server', async function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
const errorMessage = 'Error Found';
await tracker.captureException(new Error(errorMessage));
const event = tracker.getCurrentEvent();
expect(event.type).to.equal('exception');
expect(event.exception.message).to.equal(errorMessage);
});
it('should create an event with a array of stacktrace ', async function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
const errorMessage = 'Error Found';
await tracker.captureException(new Error(errorMessage));
const event = tracker.getCurrentEvent();
expect(event.type).to.equal('exception');
expect(event.exception.message).to.equal(errorMessage);
expect(event.exception.stacktrace).to.be.an('object');
expect(event.exception.stacktrace.frames).to.be.an('array');
});
it('should create an event with the object of the stacktrace in place', async function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
const errorMessage = 'Error Found';
await tracker.captureException(new Error(errorMessage));
const event = tracker.getCurrentEvent();
const frame = event.exception.stacktrace.frames[0];
expect(frame).to.have.property('methodName');
expect(frame).to.have.property('lineNumber');
expect(frame).to.have.property('columnNumber');
expect(frame).to.have.property('fileName');
});
it('should create an event and new event should have different id ', async function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
let event, newEvent;
const errorMessage = 'Error Found';
const errorMessageObj = 'Object Error Found';
await tracker.captureMessage(errorMessage).then(evt => {
event = evt.data;
});
await tracker.captureException(new Error(errorMessageObj)).then(evt => {
newEvent = evt.data;
});
// ensure that the first event have a type message, same error message
expect(event.type).to.equal('message');
expect(event.content.message).to.equal(errorMessage);
// ensure that the second event have a type exception, same error message
expect(newEvent.type).to.equal('exception');
expect(newEvent.content.message).to.equal(errorMessageObj);
// confim their eventId is different
expect(event._id).to.not.equal(newEvent._id);
});
it('should create an event that has timeline and new event having tags', async function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
let event, newEvent;
const errorMessage = 'Error Found';
const errorMessageObj = 'Object Error Found';
// add a timelie action to the first event
tracker.addToTimeline(customTimeline);
await tracker.captureMessage(errorMessage).then(evt => {
event = evt.data;
});
// add a tag to the second event
tracker.setTag('test', 'content');
await tracker.captureException(new Error(errorMessageObj)).then(evt => {
newEvent = evt.data;
});
// ensure that the first event have a type message, same error message and two timeline (the custom and the generic one)
expect(event.type).to.equal('message');
expect(event.content.message).to.equal(errorMessage);
expect(event.timeline).to.have.lengthOf(2);
expect(event.tags).to.have.lengthOf(1); // the default event tag added
// ensure that the second event have a type exception, same error message and one timeline (the generic one)
expect(newEvent.type).to.equal('exception');
expect(newEvent.content.message).to.equal(errorMessageObj);
expect(newEvent.timeline).to.have.lengthOf(1);
expect(newEvent.tags).to.have.lengthOf(2); // the default and custom tag
});
});
describe('SDK Version', function() {
it('should contain version number and sdk name in captured message', function() {
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key
);
const errorMessage = 'Uncaught Exception';
tracker.captureMessage(errorMessage);
const event = tracker.getCurrentEvent();
expect(event.sdk.name).to.be.a('string');
expect(event.sdk.version).to.match(/(([0-9])+\.([0-9])+\.([0-9])+)/); // confirm that the versiion follows the patter XX.XX.XX where X is a non negative integer
});
});
describe('Code Capture Snippet', function() {
it('should add code capture to stack trace when flag is passed in options', async function() {
const options = { captureCodeSnippet: true };
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key,
options
);
let event = null;
const errorMessageObj = 'Object Error Found';
await tracker.captureException(new Error(errorMessageObj)).then(evt => {
event = evt.data;
});
const incidentFrame = event.content.stacktrace.frames[0];
expect(incidentFrame).to.have.property('linesBeforeError');
expect(incidentFrame).to.have.property('linesAfterError');
expect(incidentFrame).to.have.property('errorLine');
});
it('should add code capture and confirm data type of fields added to frame', async function() {
const options = { captureCodeSnippet: true };
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key,
options
);
let event = null;
const errorMessageObj = 'Object Error Found';
await tracker.captureException(new Error(errorMessageObj)).then(evt => {
event = evt.data;
});
const incidentFrame = event.content.stacktrace.frames[0];
expect(incidentFrame.errorLine).to.be.a('string');
expect(incidentFrame.linesBeforeError).to.be.an('array');
expect(incidentFrame.linesAfterError).to.be.an('array');
});
it('should not add code capture to stack trace when flag is passed in options', async function() {
const options = { captureCodeSnippet: false };
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key,
options
);
let event = null;
const errorMessageObj = 'Object Error Found';
await tracker.captureException(new Error(errorMessageObj)).then(evt => {
event = evt.data;
});
const incidentFrame = event.content.stacktrace.frames[0];
expect(incidentFrame).to.not.have.property('linesBeforeError');
expect(incidentFrame).to.not.have.property('linesAfterError');
expect(incidentFrame).to.not.have.property('errorLine');
});
it('should add code capture to stack trace by default when unwanted flag is passed in options', async function() {
const options = { captureCodeSnippet: 'heyy' }; // expects a true or false but it defaults to true
const tracker = new FyipeTracker(
API_URL,
errorTracker._id,
errorTracker.key,
options
);
let event = null;
const errorMessageObj = 'Object Error Found';
await tracker.captureException(new Error(errorMessageObj)).then(evt => {
event = evt.data;
});
const incidentFrame = event.content.stacktrace.frames[0];
expect(incidentFrame).to.have.property('linesBeforeError');
expect(incidentFrame).to.have.property('linesAfterError');
expect(incidentFrame).to.have.property('errorLine');
});
});