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:
Viktar Donich
2018-04-19 22:18:31 -07:00
parent 745311d150
commit d155b8bd4a
6 changed files with 84 additions and 8 deletions

View File

@@ -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>

View File

@@ -230,6 +230,8 @@
};
});
});
}).then(() => {
this.$.reporting.dashboardDisplayed();
}).catch(err => {
this._loading = false;
console.warn(err);

View File

@@ -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>

View File

@@ -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);
})();

View File

@@ -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);

View File

@@ -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;