mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
468 lines
13 KiB
JavaScript
468 lines
13 KiB
JavaScript
var sinon = require('sinon');
|
|
var chai = require("chai")
|
|
chai.use(require("sinon-chai"))
|
|
var expect = chai.expect
|
|
var rewire = require('rewire');
|
|
|
|
describe('analytics', function() {
|
|
// Mocks
|
|
var amplitudeMock = sinon.stub();
|
|
var googleAnalyticsMock = sinon.stub();
|
|
var amplitudeTrack = sinon.stub().returns({
|
|
catch: function () { return true; }
|
|
});
|
|
var googleEvent = sinon.stub().returns({
|
|
send: function() { }
|
|
});
|
|
var googleItem = sinon.stub().returns({
|
|
send: function() { }
|
|
});
|
|
var googleTransaction = sinon.stub().returns({
|
|
item: googleItem
|
|
});
|
|
|
|
afterEach(function(){
|
|
amplitudeMock.reset();
|
|
amplitudeTrack.reset();
|
|
googleEvent.reset();
|
|
googleTransaction.reset();
|
|
googleItem.reset();
|
|
});
|
|
|
|
describe('init', function() {
|
|
var analytics = rewire('../../website/src/analytics');
|
|
|
|
it('throws an error if no options are passed in', function() {
|
|
expect(analytics).to.throw('No options provided');
|
|
});
|
|
|
|
it('registers amplitude with token', function() {
|
|
analytics.__set__('Amplitude', amplitudeMock);
|
|
var options = {
|
|
amplitudeToken: 'token'
|
|
};
|
|
analytics(options);
|
|
|
|
expect(amplitudeMock).to.be.calledOnce;
|
|
expect(amplitudeMock).to.be.calledWith('token');
|
|
});
|
|
|
|
it('registers google analytics with token', function() {
|
|
analytics.__set__('googleAnalytics', googleAnalyticsMock);
|
|
var options = {
|
|
googleAnalytics: 'token'
|
|
};
|
|
analytics(options);
|
|
|
|
expect(googleAnalyticsMock).to.be.calledOnce;
|
|
expect(googleAnalyticsMock).to.be.calledWith('token');
|
|
});
|
|
});
|
|
|
|
describe('track', function() {
|
|
|
|
var analyticsData, event_type;
|
|
var analytics = rewire('../../website/src/analytics');
|
|
var initializedAnalytics;
|
|
|
|
beforeEach(function() {
|
|
analytics.__set__('Amplitude', amplitudeMock);
|
|
initializedAnalytics = analytics({amplitudeToken: 'token'});
|
|
analytics.__set__('amplitude.track', amplitudeTrack);
|
|
analytics.__set__('ga.event', googleEvent);
|
|
|
|
event_type = 'Cron';
|
|
analyticsData = {
|
|
category: 'behavior',
|
|
uuid: 'unique-user-id',
|
|
resting: true,
|
|
cronCount: 5
|
|
}
|
|
});
|
|
|
|
context('Amplitude', function() {
|
|
it('tracks event in amplitude', function() {
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(amplitudeTrack).to.be.calledOnce;
|
|
expect(amplitudeTrack).to.be.calledWith({
|
|
event_type: 'Cron',
|
|
user_id: 'unique-user-id',
|
|
platform: 'server',
|
|
event_properties: {
|
|
category: 'behavior',
|
|
resting: true,
|
|
cronCount: 5
|
|
}
|
|
});
|
|
});
|
|
|
|
it('uses a dummy user id if none is provided', function() {
|
|
delete analyticsData.uuid;
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(amplitudeTrack).to.be.calledOnce;
|
|
expect(amplitudeTrack).to.be.calledWith({
|
|
event_type: 'Cron',
|
|
user_id: 'no-user-id-was-provided',
|
|
platform: 'server',
|
|
event_properties: {
|
|
category: 'behavior',
|
|
resting: true,
|
|
cronCount: 5
|
|
}
|
|
});
|
|
});
|
|
|
|
it('sends english item name for gear if itemKey is provided', function() {
|
|
analyticsData.itemKey = 'headAccessory_special_foxEars'
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(amplitudeTrack).to.be.calledOnce;
|
|
expect(amplitudeTrack).to.be.calledWith({
|
|
event_type: 'Cron',
|
|
user_id: 'unique-user-id',
|
|
platform: 'server',
|
|
event_properties: {
|
|
itemKey: 'headAccessory_special_foxEars',
|
|
itemName: 'Fox Ears',
|
|
category: 'behavior',
|
|
resting: true,
|
|
cronCount: 5
|
|
}
|
|
});
|
|
});
|
|
|
|
it('sends english item name for egg if itemKey is provided', function() {
|
|
analyticsData.itemKey = 'Wolf'
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(amplitudeTrack).to.be.calledOnce;
|
|
expect(amplitudeTrack).to.be.calledWith({
|
|
event_type: 'Cron',
|
|
user_id: 'unique-user-id',
|
|
platform: 'server',
|
|
event_properties: {
|
|
itemKey: 'Wolf',
|
|
itemName: 'Wolf Egg',
|
|
category: 'behavior',
|
|
resting: true,
|
|
cronCount: 5
|
|
}
|
|
});
|
|
});
|
|
|
|
it('sends english item name for food if itemKey is provided', function() {
|
|
analyticsData.itemKey = 'Cake_Skeleton'
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(amplitudeTrack).to.be.calledOnce;
|
|
expect(amplitudeTrack).to.be.calledWith({
|
|
event_type: 'Cron',
|
|
user_id: 'unique-user-id',
|
|
platform: 'server',
|
|
event_properties: {
|
|
itemKey: 'Cake_Skeleton',
|
|
itemName: 'Bare Bones Cake',
|
|
category: 'behavior',
|
|
resting: true,
|
|
cronCount: 5
|
|
}
|
|
});
|
|
});
|
|
|
|
it('sends english item name for hatching potion if itemKey is provided', function() {
|
|
analyticsData.itemKey = 'Golden'
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(amplitudeTrack).to.be.calledOnce;
|
|
expect(amplitudeTrack).to.be.calledWith({
|
|
event_type: 'Cron',
|
|
user_id: 'unique-user-id',
|
|
platform: 'server',
|
|
event_properties: {
|
|
itemKey: 'Golden',
|
|
itemName: 'Golden Hatching Potion',
|
|
category: 'behavior',
|
|
resting: true,
|
|
cronCount: 5
|
|
}
|
|
});
|
|
});
|
|
|
|
it('sends english item name for quest if itemKey is provided', function() {
|
|
analyticsData.itemKey = 'atom1'
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(amplitudeTrack).to.be.calledOnce;
|
|
expect(amplitudeTrack).to.be.calledWith({
|
|
event_type: 'Cron',
|
|
user_id: 'unique-user-id',
|
|
platform: 'server',
|
|
event_properties: {
|
|
itemKey: 'atom1',
|
|
itemName: 'Attack of the Mundane, Part 1: Dish Disaster!',
|
|
category: 'behavior',
|
|
resting: true,
|
|
cronCount: 5
|
|
}
|
|
});
|
|
});
|
|
|
|
it('sends english item name for purchased spell if itemKey is provided', function() {
|
|
analyticsData.itemKey = 'seafoam'
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(amplitudeTrack).to.be.calledOnce;
|
|
expect(amplitudeTrack).to.be.calledWith({
|
|
event_type: 'Cron',
|
|
user_id: 'unique-user-id',
|
|
platform: 'server',
|
|
event_properties: {
|
|
itemKey: 'seafoam',
|
|
itemName: 'Seafoam',
|
|
category: 'behavior',
|
|
resting: true,
|
|
cronCount: 5
|
|
}
|
|
});
|
|
});
|
|
|
|
it('sends user data if provided', function() {
|
|
var stats = { class: 'wizard', exp: 5, gp: 23, hp: 10, lvl: 4, mp: 30 };
|
|
var user = {
|
|
stats: stats,
|
|
contributor: { level: 1 },
|
|
purchased: { plan: { planId: 'foo-plan' } },
|
|
flags: {tour: {intro: -2}},
|
|
habits: [{_id: 'habit'}],
|
|
dailys: [{_id: 'daily'}],
|
|
todos: [{_id: 'todo'}],
|
|
rewards: [{_id: 'reward'}]
|
|
};
|
|
|
|
analyticsData.user = user;
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(amplitudeTrack).to.be.calledOnce;
|
|
expect(amplitudeTrack).to.be.calledWith({
|
|
event_type: 'Cron',
|
|
user_id: 'unique-user-id',
|
|
platform: 'server',
|
|
event_properties: {
|
|
category: 'behavior',
|
|
resting: true,
|
|
cronCount: 5
|
|
},
|
|
user_properties: {
|
|
Class: 'wizard',
|
|
Experience: 5,
|
|
Gold: 23,
|
|
Health: 10,
|
|
Level: 4,
|
|
Mana: 30,
|
|
contributorLevel: 1,
|
|
subscription: 'foo-plan',
|
|
tutorialComplete: true,
|
|
"Number Of Tasks": {
|
|
todos: 1,
|
|
dailys: 1,
|
|
habits: 1,
|
|
rewards: 1
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
context('Google Analytics', function() {
|
|
it('tracks event in google analytics', function() {
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(googleEvent).to.be.calledOnce;
|
|
expect(googleEvent).to.be.calledWith({
|
|
ec: 'behavior',
|
|
ea: 'Cron'
|
|
});
|
|
});
|
|
|
|
it('if itemKey property is provided, use as label', function() {
|
|
analyticsData.itemKey = 'some item';
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(googleEvent).to.be.calledOnce;
|
|
expect(googleEvent).to.be.calledWith({
|
|
ec: 'behavior',
|
|
ea: 'Cron',
|
|
el: 'some item'
|
|
});
|
|
});
|
|
|
|
it('if gaLabel property is provided, use as label (overrides itemKey)', function() {
|
|
analyticsData.value = 'some value';
|
|
analyticsData.itemKey = 'some item';
|
|
analyticsData.gaLabel = 'some label';
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(googleEvent).to.be.calledOnce;
|
|
expect(googleEvent).to.be.calledWith({
|
|
ec: 'behavior',
|
|
ea: 'Cron',
|
|
el: 'some label'
|
|
});
|
|
});
|
|
|
|
it('if goldCost property is provided, use as value', function() {
|
|
analyticsData.goldCost = 5;
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(googleEvent).to.be.calledOnce;
|
|
expect(googleEvent).to.be.calledWith({
|
|
ec: 'behavior',
|
|
ea: 'Cron',
|
|
ev: 5
|
|
});
|
|
});
|
|
|
|
it('if gemCost property is provided, use as value (overrides goldCost)', function() {
|
|
analyticsData.gemCost = 7;
|
|
analyticsData.goldCost = 5;
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(googleEvent).to.be.calledOnce;
|
|
expect(googleEvent).to.be.calledWith({
|
|
ec: 'behavior',
|
|
ea: 'Cron',
|
|
ev: 7
|
|
});
|
|
});
|
|
|
|
it('if gaValue property is provided, use as value (overrides gemCost)', function() {
|
|
analyticsData.gemCost = 7;
|
|
analyticsData.gaValue = 5;
|
|
|
|
initializedAnalytics.track(event_type, analyticsData);
|
|
|
|
expect(googleEvent).to.be.calledOnce;
|
|
expect(googleEvent).to.be.calledWith({
|
|
ec: 'behavior',
|
|
ea: 'Cron',
|
|
ev: 5
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('trackPurchase', function() {
|
|
|
|
var purchaseData;
|
|
|
|
var analytics = rewire('../../website/src/analytics');
|
|
var initializedAnalytics;
|
|
|
|
beforeEach(function() {
|
|
analytics.__set__('Amplitude', amplitudeMock);
|
|
initializedAnalytics = analytics({amplitudeToken: 'token', googleAnalytics: 'token'});
|
|
analytics.__set__('amplitude.track', amplitudeTrack);
|
|
analytics.__set__('ga.event', googleEvent);
|
|
analytics.__set__('ga.transaction', googleTransaction);
|
|
|
|
purchaseData = {
|
|
uuid: 'user-id',
|
|
sku: 'paypal-checkout',
|
|
paymentMethod: 'PayPal',
|
|
itemPurchased: 'Gems',
|
|
purchaseValue: 8,
|
|
purchaseType: 'checkout',
|
|
gift: false,
|
|
quantity: 1
|
|
}
|
|
|
|
});
|
|
|
|
context('Amplitude', function() {
|
|
|
|
it('calls amplitude.track', function() {
|
|
initializedAnalytics.trackPurchase(purchaseData);
|
|
|
|
expect(amplitudeTrack).to.be.calledOnce;
|
|
expect(amplitudeTrack).to.be.calledWith({
|
|
event_type: 'purchase',
|
|
user_id: 'user-id',
|
|
platform: 'server',
|
|
event_properties: {
|
|
paymentMethod: 'PayPal',
|
|
sku: 'paypal-checkout',
|
|
gift: false,
|
|
itemPurchased: 'Gems',
|
|
purchaseType: 'checkout',
|
|
quantity: 1
|
|
},
|
|
revenue: 8
|
|
});
|
|
});
|
|
});
|
|
|
|
context('Google Analytics', function() {
|
|
|
|
it('calls ga.event', function() {
|
|
initializedAnalytics.trackPurchase(purchaseData);
|
|
|
|
expect(googleEvent).to.be.calledOnce;
|
|
expect(googleEvent).to.be.calledWith({
|
|
ec: 'commerce',
|
|
ea: 'checkout',
|
|
el: 'PayPal',
|
|
ev: 8
|
|
});
|
|
});
|
|
|
|
it('calls ga.transaction', function() {
|
|
initializedAnalytics.trackPurchase(purchaseData);
|
|
|
|
expect(googleTransaction).to.be.calledOnce;
|
|
expect(googleTransaction).to.be.calledWith(
|
|
'user-id',
|
|
8
|
|
);
|
|
expect(googleItem).to.be.calledOnce;
|
|
expect(googleItem).to.be.calledWith(
|
|
8,
|
|
1,
|
|
'paypal-checkout',
|
|
'Gems',
|
|
'checkout'
|
|
);
|
|
});
|
|
|
|
it('appends gift to variation of ga.transaction.item if gift is true', function() {
|
|
|
|
purchaseData.gift = true;
|
|
initializedAnalytics.trackPurchase(purchaseData);
|
|
|
|
expect(googleItem).to.be.calledOnce;
|
|
expect(googleItem).to.be.calledWith(
|
|
8,
|
|
1,
|
|
'paypal-checkout',
|
|
'Gems',
|
|
'checkout - Gift'
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|