Closed lancejpollard closed 10 years ago
/cc @ianstormtaylor there's actually a bunch of cases like we're describing:
AdRoll.prototype.initialize = function(page){
window.adroll_adv_id = this.options.advId;
window.adroll_pix_id = this.options.pixId;
window.__adroll_loaded = true;
var name = useHttps() ? 'https' : 'http';
this.load(name, this.ready);
};
Awesm.prototype.initialize = function(page){
window.AWESM = { api_key: this.options.apiKey };
this.load(this.ready);
};
BugHerd.prototype.initialize = function(page){
window.BugHerdConfig = { feedback: { hide: !this.options.showFeedbackTab }};
this.load(this.ready);
};
Chartbeat.prototype.initialize = function(page){
var self = this;
window._sf_async_config = window._sf_async_config || {};
window._sf_async_config.useCanonical = true;
defaults(window._sf_async_config, this.options);
onBody(function(){
window._sf_endpt = new Date().getTime();
// Note: Chartbeat depends on document.body existing so the script does
// not load until that is confirmed. Otherwise it may trigger errors.
self.load(self.ready);
});
};
ChurnBee.prototype.initialize = function(page){
push('_setApiKey', this.options.apiKey);
this.load(this.ready);
};
Clicky.prototype.initialize = function(page){
var user = this.analytics.user();
window.clicky_site_ids = window.clicky_site_ids || [this.options.siteId];
this.identify(new Identify({
userId: user.id(),
traits: user.traits()
}));
this.load(this.ready);
};
Comscore.prototype.initialize = function(page){
window._comscore = window._comscore || [this.options];
var name = useHttps() ? 'https' : 'http';
this.load(name, this.ready);
};
CrazyEgg.prototype.initialize = function(page){
var number = this.options.accountNumber;
var path = number.slice(0,4) + '/' + number.slice(4);
var cache = Math.floor(new Date().getTime() / 3600000);
this.load({ path: path, cache: cache }, this.ready);
};
Curebit.prototype.initialize = function(page){
push('init', { site_id: this.options.siteId, server: this.options.server });
this.load(this.ready);
// throttle the call to `page` since curebit needs to append an iframe
this.page = throttle(bind(this, this.page), 250);
};
Customerio.prototype.initialize = function(page){
window._cio = window._cio || [];
(function(){var a,b,c; a = function(f){return function(){window._cio.push([f].concat(Array.prototype.slice.call(arguments,0))); }; }; b = ['identify', 'track']; for (c = 0; c < b.length; c++) {window._cio[b[c]] = a(b[c]); } })();
this.load(this.ready);
};
Drip.prototype.initialize = function(page){
window._dcq = window._dcq || [];
window._dcs = window._dcs || {};
window._dcs.account = this.options.account;
this.load(this.ready);
};
Errorception.prototype.initialize = function(page){
window._errs = window._errs || [this.options.projectId];
onError(push);
this.load(this.ready);
};
Evergage.prototype.initialize = function(page){
var account = this.options.account;
var dataset = this.options.dataset;
window._aaq = window._aaq || [];
push('setEvergageAccount', account);
push('setDataset', dataset);
push('setUseSiteConfig', true);
this.load(this.ready);
};
Facebook.prototype.initialize = function(page){
window._fbq = window._fbq || [];
this.load(this.ready);
window._fbq.loaded = true;
};
Alexa.prototype.initialize = function(page){
var self = this;
window._atrk_opts = {
atrk_acct: this.options.account,
domain: this.options.domain,
dynamic: this.options.dynamic
};
this.load(function(){
window.atrk();
self.ready();
});
};
init script multiple times, don't know what it will do if we don't reload the library more than once
Amplitude.prototype.initialize = function(page){
(function(e,t){var r=e.amplitude||{}; r._q=[];function i(e){r[e]=function(){r._q.push([e].concat(Array.prototype.slice.call(arguments,0)));};} var s=["init","logEvent","setUserId","setGlobalUserProperties","setVersionName","setDomain"]; for (var c=0;c<s.length;c++){i(s[c]);}e.amplitude=r;})(window,document);
window.amplitude.init(this.options.apiKey);
this.load(this.ready);
};
Awesomatic.prototype.initialize = function(page){
var self = this;
var user = this.analytics.user();
var id = user.id();
var options = user.traits();
options.appId = this.options.appId;
if (id) options.user_id = id;
this.load(function(){
window.Awesomatic.initialize(options, function(){
self.ready(); // need to wait for initialize to callback
});
});
};
Bing.prototype.initialize = function(page){
if (!window.mstag) {
window.mstag = {
loadTag: noop,
time: (new Date()).getTime(),
_write: writeToAppend
};
};
var self = this;
onbody(function(){
self.load(function(){
var loaded = bind(self, self.loaded);
when(loaded, self.ready);
});
});
};
Bronto.prototype.initialize = function(page){
var self = this;
var params = qs.parse(window.location.search);
if (!params._bta_tid && !params._bta_c) {
this.debug('missing tracking URL parameters `_bta_tid` and `_bta_c`.');
}
this.load(function(){
var opts = self.options;
self.bta = new window.__bta(opts.siteId);
if (opts.host) self.bta.setHost(opts.host);
self.ready();
});
};
Bugsnag.prototype.initialize = function(page){
var self = this;
this.load(function(){
window.Bugsnag.apiKey = self.options.apiKey;
self.ready();
});
};
ClickTale.prototype.initialize = function(page){
var self = this;
window.WRInitTime = date.getTime();
onBody(function(body){
body.appendChild(domify('<div id="ClickTaleDiv" style="display: none;">'));
});
var http = this.options.httpCdnUrl;
var https = this.options.httpsCdnUrl;
if (useHttps() && !https) return this.debug('https option required');
var src = useHttps() ? https : http;
this.load({ src: src }, function(){
window.ClickTale(
self.options.projectId,
self.options.recordingRatio,
self.options.partitionId
);
self.ready();
});
};
/cc @ianstormtaylor putting thoughts here on how to handle only loading once
awesomatic
What we could do is create a
snippet
function that is similar to the originalload
function, and it has everything we only want to run once for tests. But then the newload
can still load all sorts of tags.So then in the tests we would just stub out
snippet
in theafter loading
block.Maybe we can clean that up somewhat so it's not repeated in every test, but we can come back to that later too. Or, we can do the
_wrapSnippet
like_wrapLoad
, that does the.loaded()
check to not run the snippet if it's already loaded.This way:
initialize
can have some initial logic for mapping options to global variables, and stuff like that.snippet
will only load onceAnother option is to rename the new
load
method to something else, and name thesnippet
method back toload
.olark
This one is tougher. It creates an iframe, which loads scripts, and the scripts attach event handlers through the
iframe.parent
. So removing the iframe causes problems. I would love to spend the time eventually to figure out how to clean up the iframe so we can recreate it for each test. But for now, it should only be created once like we're saying for everything else.So then it's like, what is happening in that
initialize
function? Or a better question:Right now, we're only testing one thing "before initialize":
This is possible because we can stub out the
window.olark
function. However, this only works to tell that the function got the correct arguments.How about this next case? Why can't we test these in "before initialize"?
The reason is, it depends on the iframe loading, whereas that
api.chat.setOperatorGroup
doesn't depend on anything: we can mock out the method.We could mock out the method for
window.olark
again, and tell that it has the corrent function, but then we would have to emit a fake event, and then see ifself._open = true
. I dunno, maybe we do that? The problem is, we would have to stub out some internal details of olark itself.What we're actually doing now, though, is waiting until the native event from olark gets emitted, which takes a second or two the first time.
The problem is, if we mock that out, we could potentially have named the event incorrectly, and so while our tests may pass, the code won't quite be right. So it seems we should still test the actual olark event.
So then it boils down to:
A couple of possibilities (there are probably way more too):
initialize
we mock out all provider methods, so we can test everything in "before initialize". This means, for more complex things like the olark expand/shrink case, we would have to mock out some of their event-emitting functionality, which could get tricky and might not result in robust-enough tests. The pro is that, we can test all of initialize in "before loaded"... more soon