diff --git a/.travis.yml b/.travis.yml index 9d4e902..9f83b60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,3 +2,4 @@ language: ruby rvm: - 2.2.3 before_install: gem install bundler -v 1.11.2 +script: rake travis diff --git a/Guardfile b/Guardfile index deea540..873a921 100644 --- a/Guardfile +++ b/Guardfile @@ -24,6 +24,12 @@ # * zeus: 'zeus rspec' (requires the server to be started separately) # * 'just' rspec: 'rspec' +guard 'jasmine' do + watch(%r{spec/javascripts/spec\.(js\.coffee|js|coffee)$}) { "spec/javascripts" } + watch(%r{spec/javascripts/.+_spec\.(js\.coffee|js|coffee)$}) + watch(%r{app/assets/javascripts/(.+?)\.(js\.coffee|js|coffee)$}) { |m| "spec/javascripts/#{m[1]}_spec.#{m[2]}" } +end + guard :rspec, cmd: "bundle exec rspec" do require "guard/rspec/dsl" dsl = Guard::RSpec::Dsl.new(self) diff --git a/Rakefile b/Rakefile index c8e19eb..e23a1c9 100644 --- a/Rakefile +++ b/Rakefile @@ -6,3 +6,11 @@ RSpec::Core::RakeTask.new(:spec) task :default => :spec require 'jasmine' load 'jasmine/tasks/jasmine.rake' + +task :travis do + ["rspec spec", "rake jasmine:ci"].each do |cmd| + puts "Starting to run #{cmd}..." + system("export DISPLAY=:99.0 && bundle exec #{cmd}") + raise "#{cmd} failed!" unless $?.exitstatus == 0 + end +end diff --git a/action_tracker.gemspec b/action_tracker.gemspec index cc3aafd..e1a6509 100644 --- a/action_tracker.gemspec +++ b/action_tracker.gemspec @@ -28,5 +28,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'guard', '~> 2.13' spec.add_development_dependency 'byebug', '~> 5.0' spec.add_development_dependency 'guard-rspec', '~> 4.6' + spec.add_development_dependency 'guard-jasmine', '~> 2.0.6' + spec.add_development_dependency 'jasmine', '~> 2.4' end diff --git a/app/assets/javascripts/Storage.js b/app/assets/javascripts/Storage.js new file mode 100644 index 0000000..01c0d98 --- /dev/null +++ b/app/assets/javascripts/Storage.js @@ -0,0 +1,69 @@ +var ActionTracker = (function(self) { + + self.Storage = function() { + var storage = []; + + function constructor() { + if(sessionStorage.getItem('action_tracker_storage')) { + getStorage(); + } else { + setStorage(); + } + } + + function getStorage() { + storage = JSON.parse(sessionStorage.getItem('action_tracker_storage')); + } + + function setStorage() { + sessionStorage.setItem('action_tracker_storage', JSON.stringify(storage)); + } + + function queue(list) { + if(list !== null) { + getStorage(); + var i; + for (i = 0; i < list.length; i += 1) { + storage.push(list[i]); + } + setStorage(); + } + } + + function dequeue() { + getStorage(); + var first = storage[0]; + storage.splice(0, 1); + setStorage(); + return first; + } + + function getFirst() { + getStorage(); + return storage[0]; + } + + function clear() { + storage = []; + setStorage(); + } + + function getLocalStorage() { + return storage; + } + + constructor(); + + return { + queue: queue, + dequeue: dequeue, + getStorage: getLocalStorage, + getFirst: getFirst, + refreshStorage: getStorage, + clear: clear, + }; + }; + + return self; + +}(ActionTracker || {})); diff --git a/app/assets/javascripts/TimeSeed.js b/app/assets/javascripts/TimeSeed.js new file mode 100644 index 0000000..a4777e5 --- /dev/null +++ b/app/assets/javascripts/TimeSeed.js @@ -0,0 +1,17 @@ +var ActionTracker = (function(self) { + self.TimeSeed = function() { + var seedDate = new Date(); + + function getTimeSeed() { + seedDate.setSeconds(seedDate.getSeconds() + 1); + return seedDate; + } + + return { + getTimeSeed: getTimeSeed + }; + }; + + return self; + +}(ActionTracker || {})); diff --git a/app/assets/javascripts/Tracker.js b/app/assets/javascripts/Tracker.js new file mode 100644 index 0000000..bc2fed2 --- /dev/null +++ b/app/assets/javascripts/Tracker.js @@ -0,0 +1,52 @@ +var ActionTracker = (function(self) { + + self.Tracker = function(trackerData, cfgOptions, callbacks) { + var userFlag = false, + user = null, + options = null, + data = null, + dataFlag = false, + logoutFlag = false; + + if(typeof cfgOptions !== 'undefined') { + options = cfgOptions; + } + + if(typeof trackerData !== 'undefined') { + if(typeof trackerData.identify !== 'undefined') { + userFlag = true; + user = new self.User(trackerData.identify, callbacks); + } + if(typeof trackerData.track !== 'undefined') { + dataFlag = true; + data = trackerData.track; + if(options.timestamp) { + data.created_at = options.seed.getTimeSeed(); + } + } + if(trackerData.logout) { + logoutFlag = true; + } + } + + function send() { + if(userFlag) { + callbacks.identify(user.getData()); + } + if(dataFlag) { + callbacks.track(data, function() { + if(logoutFlag) { + callbacks.logout(); + } + }); + } + } + + return { + send: send + }; + }; + + return self; + +}(ActionTracker || {})); diff --git a/app/assets/javascripts/User.js b/app/assets/javascripts/User.js new file mode 100644 index 0000000..25827cc --- /dev/null +++ b/app/assets/javascripts/User.js @@ -0,0 +1,25 @@ +var ActionTracker = (function(self) { + + self.User = function(userData, userCallbacks) { + var data = userData, + callbacks = userCallbacks; + + data.id = callbacks.generateID(data.email); + + function getData() { + return data; + } + + function getCallbacks() { + return callbacks; + } + + return { + getData: getData, + getCallbacks: getCallbacks + }; + }; + + return self; + +}(ActionTracker || {})); diff --git a/app/assets/javascripts/action_tracker.js b/app/assets/javascripts/action_tracker.js index ff1ff7b..1141bf8 100644 --- a/app/assets/javascripts/action_tracker.js +++ b/app/assets/javascripts/action_tracker.js @@ -1,35 +1,37 @@ +//= require Storage +//= require TimeSeed +//= require User +//= require Tracker -var ActionTracker = function () { - - 'use strict'; +var ActionTracker = (function(self) { var storage, timeSeed, options = {}, callbacks = {}; - function constructor() { - storage = new Storage(); - timeSeed = new TimeSeed(); - } - - function setCallbacks(callbacksObj) { + self.callbacks = function(callbacksObj) { callbacks = callbacksObj; - } + }; - function start(list, cfgOptions) { + self.new = function(list, cfgOptions) { if(typeof cfgOptions !== 'undefined') { options = cfgOptions; } storage.queue(list); - } + }; - function process() { + self.push = function() { var tracker; while(typeof storage.getFirst() !== 'undefined') { - tracker = new Tracker(storage.dequeue(), trackerOptions()); + tracker = new self.Tracker(storage.dequeue(), trackerOptions(), callbacks); tracker.send(); } + }; + + function constructor() { + storage = new self.Storage(); + timeSeed = new self.TimeSeed(); } function trackerOptions() { @@ -41,125 +43,8 @@ var ActionTracker = function () { return trackerParams; } - function Storage() { - - this.storage = []; - - this.constructor = function() { - if(sessionStorage.getItem('action_tracker_storage')) { - this.getStorage(); - } else { - this.setStorage(); - } - }; - - this.getStorage = function() { - this.storage = JSON.parse(sessionStorage.getItem('action_tracker_storage')); - }; - - this.setStorage = function() { - sessionStorage.setItem('action_tracker_storage', JSON.stringify(this.storage)); - }; - - this.queue = function(list) { - if(list != null) { - this.getStorage(); - var i; - for (i = 0; i < list.length; i += 1) { - this.storage.push(list[i]); - } - this.setStorage(); - } - }; - - this.dequeue = function() { - this.getStorage(); - var first = this.storage[0]; - this.storage.splice(0, 1); - this.setStorage(); - return first; - }; - - this.getFirst = function() { - this.getStorage(); - return this.storage[0]; - }; - - this.constructor(); - } - - function Tracker(trackerData, cfgOptions) { - - this.userFlag = false; - this.user = null; - this.options = null; - - var data = null; - var dataFlag = false; - var logoutFlag = false; - - if(typeof cfgOptions !== 'undefined') { - this.options = cfgOptions; - } - - if(typeof trackerData !== 'undefined') { - if(typeof trackerData.identify !== 'undefined') { - this.userFlag = true; - this.user = new User(trackerData.identify); - } - if(typeof trackerData.track !== 'undefined') { - dataFlag = true; - data = trackerData.track; - if(this.options.timestamp) { - data.created_at = this.options.seed.getTimeSeed(); - } - } - if(trackerData.logout) { - logoutFlag = true; - } - } - - this.send = function() { - if(this.userFlag) { - callbacks.identify(this.user.getData()); - } - if(dataFlag) { - callbacks.track(data, function() { - if(logoutFlag) { - callbacks.logout(); - } - }); - } - }; - } - - function User(userData) { - this.data = userData; - this.data.id = callbacks.generateID(this.data.email); - - this.getData = function() { - return this.data; - }; - } - - function TimeSeed() { - this.seed_date = new Date(); - - this.getTimeSeed = function() { - this.seed_date.setSeconds(this.seed_date.getSeconds() + 1); - return this.seed_date; - }; - } - constructor(); - return { - Tracker: Tracker, - User: User, - Storage: Storage, - new: start, - push: process, - callbacks: setCallbacks - }; + return self; -}(); +}(ActionTracker || {})); diff --git a/spec/javascripts/action_tracker_storage_spec.js b/spec/javascripts/action_tracker_storage_spec.js new file mode 100644 index 0000000..ed50ef6 --- /dev/null +++ b/spec/javascripts/action_tracker_storage_spec.js @@ -0,0 +1,53 @@ + +var ActionTracker = ActionTracker || {}; + +describe('Action Tracker Storage', function() { + + var storage; + + beforeEach(function() { + storage = new ActionTracker.Storage(); + storage.clear(); + }); + + it('initializes empty', function() { + expect(typeof storage.getFirst()).toEqual('undefined'); + expect(storage.getStorage()).toEqual([]); + }); + + it('add item to storage', function() { + storage.queue(['foo']); + storage.queue(['bar']); + expect(storage.getStorage()).toEqual(['foo', 'bar']); + }); + + it('get item from storage', function() { + storage.queue(['foo']); + storage.queue(['bar']); + + expect(storage.getFirst()).toEqual('foo'); + expect(storage.dequeue()).toEqual('foo'); + expect(storage.getFirst()).toEqual('bar'); + expect(storage.dequeue()).toEqual('bar'); + + expect(storage.getStorage()).toEqual([]); + }); + + it('shares same index in session storage', function() { + storage.queue(['foo']); + storage.queue(['bar']); + + var newStorage = new ActionTracker.Storage(); + + expect(newStorage.getFirst()).toEqual('foo'); + expect(newStorage.dequeue()).toEqual('foo'); + expect(newStorage.getFirst()).toEqual('bar'); + expect(newStorage.dequeue()).toEqual('bar'); + + storage.refreshStorage(); + + expect(storage.getStorage()).toEqual([]); + expect(newStorage.getStorage()).toEqual([]); + }); + +}); diff --git a/spec/javascripts/action_tracker_time_seed_spec.js b/spec/javascripts/action_tracker_time_seed_spec.js new file mode 100644 index 0000000..03e890c --- /dev/null +++ b/spec/javascripts/action_tracker_time_seed_spec.js @@ -0,0 +1,20 @@ + +var ActionTracker = ActionTracker || {}; + +describe('Action Tracker Time Seed', function() { + + var timeSeed; + + beforeEach(function() { + timeSeed = new ActionTracker.TimeSeed(); + }); + + it('increase time by 1 sec every time it gets called', function(){ + var second = timeSeed.getTimeSeed().getSeconds(); + expect(timeSeed.getTimeSeed().getSeconds() - second).toEqual(1); + expect(timeSeed.getTimeSeed().getSeconds() - second).toEqual(2); + expect(timeSeed.getTimeSeed().getSeconds() - second).toEqual(3); + expect(timeSeed.getTimeSeed().getSeconds() - second).toEqual(4); + }); + +}); diff --git a/spec/javascripts/action_tracker_tracker_spec.js b/spec/javascripts/action_tracker_tracker_spec.js new file mode 100644 index 0000000..a08c56a --- /dev/null +++ b/spec/javascripts/action_tracker_tracker_spec.js @@ -0,0 +1,17 @@ + +var ActionTracker = ActionTracker || {}; + +describe('Action Tracker Tracker', function() { + + beforeEach(function() { + storage = new ActionTracker.Tracker(); + }); + + xit('sets tracker data properly', function(){}); + xit('sets options data properly', function(){}); + xit('sets callbacks properly', function(){}); + xit('identifies user', function(){}); + xit('logout user', function(){}); + xit('tracks action', function(){}); + +}); diff --git a/spec/javascripts/action_tracker_user_spec.js b/spec/javascripts/action_tracker_user_spec.js new file mode 100644 index 0000000..056d817 --- /dev/null +++ b/spec/javascripts/action_tracker_user_spec.js @@ -0,0 +1,45 @@ + +var ActionTracker = ActionTracker || {}; + +describe('Action Tracker User', function() { + + var user; + + beforeEach(function(){ + user = new ActionTracker.User({ + name: 'John Doe', + email: 'john@doe.net' + }, + { + generateID: function(email) { + return email + 'here_we_go' + } + } + ); + }); + + it('set callbacks properly', function(){ + expect(typeof user.getCallbacks().generateID).toEqual('function'); + }); + + it('sets user data properly', function(){ + expect(user.getData()).toEqual( + jasmine.objectContaining( + { + name: 'John Doe', + email: 'john@doe.net' + } + ) + ); + }); + + it('generates user ID properly', function(){ + expect(user.getData()).toEqual( + jasmine.objectContaining( + { + id: 'john@doe.nethere_we_go' + } + ) + ); + }); +});