Defer metrics reporting until plugins are loaded

Cache all events reported via gr-reporting instances in closure in order
to share the cahce. Flush the cache on next reporting after plugins are
loaded.

This allows plugins to receive all reporting events and unlocks Google
Analytics integration.

Change-Id: Ic0c1d37667208bfd55bbf77e2138d0170f7fb097
This commit is contained in:
Viktar Donich
2016-10-05 11:58:58 -07:00
parent dd2852020b
commit e8680ea43a
8 changed files with 143 additions and 4 deletions

View File

@@ -15,6 +15,7 @@ limitations under the License.
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<dom-module id="gr-reporting">
<script src="gr-reporting.js"></script>

View File

@@ -33,6 +33,8 @@
var CHANGE_VIEW_REGEX = /^\/c\/\d+\/?\d*$/;
var DIFF_VIEW_REGEX = /^\/c\/\d+\/\d+\/.+$/;
var pending = [];
Polymer({
is: 'gr-reporting',
@@ -40,7 +42,7 @@
_baselines: {
type: Array,
value: function() { return {}; },
}
},
},
get performanceTiming() {
@@ -51,8 +53,13 @@
return Math.round(10 * window.performance.now()) / 10;
},
reporter: function(type, category, eventName, eventValue) {
eventValue = eventValue;
reporter: function() {
var report = (Gerrit._arePluginsLoaded() && !pending.length) ?
this.defaultReporter : this.cachingReporter;
report.apply(this, arguments);
},
defaultReporter: function(type, category, eventName, eventValue) {
var detail = {
type: type,
category: category,
@@ -63,6 +70,19 @@
console.log(eventName + ': ' + eventValue);
},
cachingReporter: function(type, category, eventName, eventValue) {
if (Gerrit._arePluginsLoaded()) {
if (pending.length) {
pending.splice(0).forEach(function(args) {
this.reporter.apply(this, args);
}, this);
}
this.reporter(type, category, eventName, eventValue);
} else {
pending.push([type, category, eventName, eventValue]);
}
},
/**
* User-perceived app start time, should be reported when the app is ready.
*/
@@ -105,6 +125,10 @@
NAVIGATION.TYPE, NAVIGATION.CATEGORY, NAVIGATION.PAGE, page);
},
pluginsLoaded: function() {
this.timeEnd('PluginsLoaded');
},
_getPathname: function() {
return window.location.pathname;
},

View File

@@ -90,6 +90,48 @@ limitations under the License.
));
});
suite('plugins', function() {
setup(function() {
element.reporter.restore();
sandbox.stub(element, 'defaultReporter');
sandbox.stub(Gerrit, '_arePluginsLoaded');
});
test('pluginsLoaded reports time', function() {
Gerrit._arePluginsLoaded.returns(true);
var nowStub = sinon.stub(element, 'now').returns(42);
element.pluginsLoaded();
assert.isTrue(element.defaultReporter.calledWithExactly(
'timing-report', 'UI Latency', 'PluginsLoaded', 42
));
});
test('caches reports if plugins are not loaded', function() {
Gerrit._arePluginsLoaded.returns(false);
element.timeEnd('foo');
assert.isFalse(element.defaultReporter.called);
});
test('reports if plugins are loaded', function() {
Gerrit._arePluginsLoaded.returns(true);
element.timeEnd('foo');
assert.isTrue(element.defaultReporter.called);
});
test('reports cached events preserving order', function() {
Gerrit._arePluginsLoaded.returns(false);
element.timeEnd('foo');
Gerrit._arePluginsLoaded.returns(true);
element.timeEnd('bar');
assert.isTrue(element.defaultReporter.firstCall.calledWith(
'timing-report', 'UI Latency', 'foo'
));
assert.isTrue(element.defaultReporter.secondCall.calledWith(
'timing-report', 'UI Latency', 'bar'
));
});
});
suite('location changed', function() {
var pathnameStub;
setup(function() {

View File

@@ -110,10 +110,12 @@
},
_loadPlugins: function(plugins) {
Gerrit._setPluginsCount(plugins.length);
for (var i = 0; i < plugins.length; i++) {
var scriptEl = document.createElement('script');
scriptEl.defer = true;
scriptEl.src = '/' + plugins[i];
scriptEl.onerror = Gerrit._pluginInstalled;
document.body.appendChild(scriptEl);
}
},

View File

@@ -78,5 +78,11 @@ limitations under the License.
assert.equal(gwtLink.href,
'http://' + location.host + '/?polygerrit=0#/c/1/1/testfile.txt@2');
});
test('sets plugins count', function() {
sandbox.stub(Gerrit, '_setPluginsCount');
element._loadPlugins([]);
assert.isTrue(Gerrit._setPluginsCount.calledWithExactly(0));
});
});
</script>

View File

@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
<dom-module id="gr-js-api-interface">
@@ -23,4 +24,3 @@ limitations under the License.
<script src="gr-js-api-interface.js"></script>
<script src="gr-public-js-api.js"></script>
</dom-module>

View File

@@ -172,5 +172,49 @@ limitations under the License.
});
});
test('_setPluginsCount', function(done) {
stub('gr-reporting', {
pluginsLoaded: function() {
assert.equal(Gerrit._pluginsPending, 0);
done();
}
});
Gerrit._setPluginsCount(0);
});
test('_arePluginsLoaded', function() {
assert.isFalse(Gerrit._arePluginsLoaded());
Gerrit._setPluginsCount(1);
assert.isFalse(Gerrit._arePluginsLoaded());
Gerrit._setPluginsCount(0);
assert.isTrue(Gerrit._arePluginsLoaded());
});
test('_pluginInstalled', function(done) {
stub('gr-reporting', {
pluginsLoaded: function() {
done();
}
});
Gerrit._setPluginsCount(2);
Gerrit._pluginInstalled();
assert.equal(Gerrit._pluginsPending, 1);
Gerrit._pluginInstalled();
});
test('install calls _pluginInstalled', function() {
var stub = sinon.stub(Gerrit, '_pluginInstalled');
Gerrit.install(function(p) { plugin = p; }, '0.1',
'http://test.com/plugins/testplugin/static/test.js');
assert.isTrue(stub.calledOnce);
stub.restore();
});
test('install calls _pluginInstalled on error', function() {
var stub = sinon.stub(Gerrit, '_pluginInstalled');
Gerrit.install(function() {}, '0.0pre-alpha');
assert.isTrue(stub.calledOnce);
stub.restore();
});
});
</script>

View File

@@ -64,6 +64,9 @@
var Gerrit = window.Gerrit || {};
// Number of plugins to initialize, -1 means 'not yet known'.
Gerrit._pluginsPending = -1;
Gerrit.getPluginName = function() {
console.warn('Gerrit.getPluginName is not supported in PolyGerrit.',
'Please use self.getPluginName() instead.');
@@ -85,12 +88,14 @@
if (opt_version && opt_version !== API_VERSION) {
console.warn('Only version ' + API_VERSION +
' is supported in PolyGerrit. ' + opt_version + ' was given.');
Gerrit._pluginInstalled();
return;
}
// TODO(andybons): Polyfill currentScript for IE10/11 (edge supports it).
var src = opt_src || (document.currentScript && document.currentScript.src);
callback(new Plugin(src));
Gerrit._pluginInstalled();
};
Gerrit.getLoggedIn = function() {
@@ -101,5 +106,20 @@
// NOOP since PolyGerrit doesnt support GWT plugins.
};
Gerrit._setPluginsCount = function(count) {
Gerrit._pluginsPending = count;
if (Gerrit._arePluginsLoaded()) {
document.createElement('gr-reporting').pluginsLoaded();
}
};
Gerrit._pluginInstalled = function() {
Gerrit._setPluginsCount(Gerrit._pluginsPending - 1);
};
Gerrit._arePluginsLoaded = function() {
return Gerrit._pluginsPending === 0;
};
window.Gerrit = Gerrit;
})(window);