Report UI latency for opening dashboard
The metric is reported as StartupDashboardDisplayed for initial load and DashboardDisplayed while navigating the app. Change-Id: I2eebc8b5987b4f7baa6ede7be690d02f939d413a
This commit is contained in:
@@ -15,10 +15,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
<link rel="import" href="../../change-list/gr-change-list/gr-change-list.html">
|
||||
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
<link rel="import" href="../gr-user-header/gr-user-header.html">
|
||||
|
||||
@@ -62,6 +63,7 @@ limitations under the License.
|
||||
sections="[[_results]]"></gr-change-list>
|
||||
</div>
|
||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||
<gr-reporting id="reporting"></gr-reporting>
|
||||
</template>
|
||||
<script src="gr-dashboard-view.js"></script>
|
||||
</dom-module>
|
||||
|
||||
@@ -230,6 +230,8 @@
|
||||
};
|
||||
});
|
||||
});
|
||||
}).then(() => {
|
||||
this.$.reporting.dashboardDisplayed();
|
||||
}).catch(err => {
|
||||
this._loading = false;
|
||||
console.warn(err);
|
||||
|
||||
@@ -55,7 +55,7 @@ limitations under the License.
|
||||
});
|
||||
const paramsChanged = element._paramsChanged.bind(element);
|
||||
sandbox.stub(element, '_paramsChanged', params => {
|
||||
paramsChanged(params).then(resolver());
|
||||
paramsChanged(params).then(() => resolver());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -262,5 +262,17 @@ limitations under the License.
|
||||
dashboard: 'dashboard',
|
||||
};
|
||||
});
|
||||
|
||||
test('params change triggers dashboardDisplayed()', () => {
|
||||
sandbox.stub(element.$.reporting, 'dashboardDisplayed');
|
||||
element.params = {
|
||||
view: Gerrit.Nav.View.DASHBOARD,
|
||||
project: 'project',
|
||||
dashboard: 'dashboard',
|
||||
};
|
||||
return paramsChangedPromise.then(() => {
|
||||
assert.isTrue(element.$.reporting.dashboardDisplayed.calledOnce);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -38,6 +38,16 @@
|
||||
CATEGORY: 'exception',
|
||||
};
|
||||
|
||||
const TIMER = {
|
||||
DASHBOARD_DISPLAYED: 'DashboardDisplayed',
|
||||
STARTUP_DASHBOARD_DISPLAYED: 'StartupDashboardDisplayed',
|
||||
PLUGINS_LOADED: 'PluginsLoaded',
|
||||
};
|
||||
|
||||
const STARTUP_TIMERS = {};
|
||||
STARTUP_TIMERS[TIMER.PLUGINS_LOADED] = 0;
|
||||
STARTUP_TIMERS[TIMER.STARTUP_DASHBOARD_DISPLAYED] = 0;
|
||||
|
||||
const INTERACTION_TYPE = 'interaction';
|
||||
|
||||
const pending = [];
|
||||
@@ -81,8 +91,8 @@
|
||||
category: String,
|
||||
|
||||
_baselines: {
|
||||
type: Array,
|
||||
value() { return {}; },
|
||||
type: Object,
|
||||
value: STARTUP_TIMERS, // Shared across all instances.
|
||||
},
|
||||
},
|
||||
|
||||
@@ -91,7 +101,7 @@
|
||||
},
|
||||
|
||||
now() {
|
||||
return Math.round(10 * window.performance.now()) / 10;
|
||||
return window.performance.now();
|
||||
},
|
||||
|
||||
reporter(...args) {
|
||||
@@ -157,13 +167,28 @@
|
||||
}
|
||||
},
|
||||
|
||||
beforeLocationChanged() {
|
||||
for (const prop of Object.keys(this._baselines)) {
|
||||
delete this._baselines[prop];
|
||||
}
|
||||
this.time(TIMER.DASHBOARD_DISPLAYED);
|
||||
},
|
||||
|
||||
locationChanged(page) {
|
||||
this.reporter(
|
||||
NAVIGATION.TYPE, NAVIGATION.CATEGORY, NAVIGATION.PAGE, page);
|
||||
},
|
||||
|
||||
dashboardDisplayed() {
|
||||
if (this._baselines.hasOwnProperty(TIMER.STARTUP_DASHBOARD_DISPLAYED)) {
|
||||
this.timeEnd(TIMER.STARTUP_DASHBOARD_DISPLAYED);
|
||||
} else {
|
||||
this.timeEnd(TIMER.DASHBOARD_DISPLAYED);
|
||||
}
|
||||
},
|
||||
|
||||
pluginsLoaded() {
|
||||
this.timeEnd('PluginsLoaded');
|
||||
this.timeEnd(TIMER.PLUGINS_LOADED);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -177,7 +202,8 @@
|
||||
* Finish named timer and report it to server.
|
||||
*/
|
||||
timeEnd(name) {
|
||||
const baseTime = this._baselines[name] || 0;
|
||||
if (!this._baselines.hasOwnProperty(name)) { return; }
|
||||
const baseTime = this._baselines[name];
|
||||
const time = Math.round(this.now() - baseTime);
|
||||
this.reporter(TIMING.TYPE, TIMING.CATEGORY, name, time);
|
||||
delete this._baselines[name];
|
||||
@@ -191,4 +217,5 @@
|
||||
window.GrReporting = GrReporting;
|
||||
// Expose onerror installation so it would be accessible from tests.
|
||||
window.GrReporting._catchErrors = catchErrors;
|
||||
window.GrReporting.STARTUP_TIMERS = Object.assign({}, STARTUP_TIMERS);
|
||||
})();
|
||||
|
||||
@@ -45,6 +45,7 @@ limitations under the License.
|
||||
sandbox = sinon.sandbox.create();
|
||||
clock = sinon.useFakeTimers(NOW_TIME);
|
||||
element = fixture('basic');
|
||||
element._baselines = Object.assign({}, GrReporting.STARTUP_TIMERS);
|
||||
fakePerformance = {
|
||||
navigationStart: 1,
|
||||
loadEventEnd: 2,
|
||||
@@ -53,6 +54,7 @@ limitations under the License.
|
||||
{get() { return fakePerformance; }});
|
||||
sandbox.stub(element, 'reporter');
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
sandbox.restore();
|
||||
clock.restore();
|
||||
@@ -76,6 +78,25 @@ limitations under the License.
|
||||
);
|
||||
});
|
||||
|
||||
test('beforeLocationChanged', () => {
|
||||
element._baselines['garbage'] = 'monster';
|
||||
sandbox.stub(element, 'time');
|
||||
element.beforeLocationChanged();
|
||||
assert.isTrue(element.time.calledWithExactly('DashboardDisplayed'));
|
||||
assert.isFalse(element._baselines.hasOwnProperty('garbage'));
|
||||
});
|
||||
|
||||
test('dashboardDisplayed', () => {
|
||||
sandbox.spy(element, 'timeEnd');
|
||||
element.dashboardDisplayed();
|
||||
assert.isFalse(
|
||||
element.timeEnd.calledWithExactly('DashboardDisplayed'));
|
||||
assert.isTrue(
|
||||
element.timeEnd.calledWithExactly('StartupDashboardDisplayed'));
|
||||
element.dashboardDisplayed();
|
||||
assert.isTrue(element.timeEnd.calledWithExactly('DashboardDisplayed'));
|
||||
});
|
||||
|
||||
test('time and timeEnd', () => {
|
||||
const nowStub = sandbox.stub(element, 'now').returns(0);
|
||||
element.time('foo');
|
||||
@@ -117,11 +138,13 @@ limitations under the License.
|
||||
|
||||
test('reports if plugins are loaded', () => {
|
||||
Gerrit._arePluginsLoaded.returns(true);
|
||||
element.timeEnd('foo');
|
||||
element.pluginsLoaded();
|
||||
assert.isTrue(element.defaultReporter.called);
|
||||
});
|
||||
|
||||
test('reports cached events preserving order', () => {
|
||||
element.time('foo');
|
||||
element.time('bar');
|
||||
Gerrit._arePluginsLoaded.returns(false);
|
||||
element.timeEnd('foo');
|
||||
Gerrit._arePluginsLoaded.returns(true);
|
||||
|
||||
@@ -199,6 +199,7 @@
|
||||
type: Object,
|
||||
value: app,
|
||||
},
|
||||
_isRedirecting: Boolean,
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
@@ -217,6 +218,7 @@
|
||||
},
|
||||
|
||||
_redirect(url) {
|
||||
this._isRedirecting = true;
|
||||
page.redirect(url);
|
||||
},
|
||||
|
||||
@@ -670,6 +672,14 @@
|
||||
params => this._generateWeblinks(params)
|
||||
);
|
||||
|
||||
page.exit('*', (ctx, next) => {
|
||||
if (!this._isRedirecting) {
|
||||
this.$.reporting.beforeLocationChanged();
|
||||
}
|
||||
this._isRedirecting = false;
|
||||
next();
|
||||
});
|
||||
|
||||
// Middleware
|
||||
page((ctx, next) => {
|
||||
document.body.scrollTop = 0;
|
||||
|
||||
Reference in New Issue
Block a user