Commit ee612212 authored by Will Chandler's avatar Will Chandler Committed by Tim Zallmann

Add 'Download' button to Performance Bar

This change adds a new 'Download' button to the Performance Bar
that allows users to download the raw Perf Bar JSON from all
completed requests.
parent d54b3423
......@@ -80,6 +80,15 @@ export default {
}
return '';
},
downloadPath() {
const data = JSON.stringify(this.requests);
const blob = new Blob([data], { type: 'text/plain' });
return window.URL.createObjectURL(blob);
},
downloadName() {
const fileName = this.requests[0].truncatedUrl;
return `${fileName}_perf_bar_${Date.now()}.json`;
},
},
mounted() {
this.currentRequest = this.requestId;
......@@ -121,6 +130,9 @@ export default {
<a :href="currentRequest.details.tracing.tracing_url">{{ s__('PerformanceBar|trace') }}</a>
</div>
<add-request v-on="$listeners" />
<div v-if="currentRequest.details" id="peek-download" class="view">
<a :download="downloadName" :href="downloadPath">{{ s__('PerformanceBar|Download') }}</a>
</div>
<request-selector
v-if="currentRequest"
:current-request="currentRequest"
......
......@@ -40,16 +40,6 @@ export default {
},
},
methods: {
truncatedUrl(requestUrl) {
const components = requestUrl.replace(/\/$/, '').split('/');
let truncated = components[components.length - 1];
if (truncated.match(/^\d+$/)) {
truncated = `${components[components.length - 2]}/${truncated}`;
}
return truncated;
},
glEmojiTag,
},
};
......@@ -63,7 +53,7 @@ export default {
:value="request.id"
class="qa-performance-bar-request"
>
{{ truncatedUrl(request.url) }}
{{ request.truncatedUrl }}
<span v-if="request.hasWarnings">(!)</span>
</option>
</select>
......
......@@ -5,9 +5,12 @@ export default class PerformanceBarStore {
addRequest(requestId, requestUrl) {
if (!this.findRequest(requestId)) {
const shortUrl = PerformanceBarStore.truncateUrl(requestUrl);
this.requests.push({
id: requestId,
url: requestUrl,
truncatedUrl: shortUrl,
details: {},
hasWarnings: false,
});
......@@ -36,4 +39,20 @@ export default class PerformanceBarStore {
canTrackRequest(requestUrl) {
return this.requests.filter(request => request.url === requestUrl).length < 2;
}
static truncateUrl(requestUrl) {
const [rootAndQuery] = requestUrl.split('#');
const [root, query] = rootAndQuery.split('?');
const components = root.replace(/\/$/, '').split('/');
let truncated = components[components.length - 1];
if (truncated.match(/^\d+$/)) {
truncated = `${components[components.length - 2]}/${truncated}`;
}
if (query) {
truncated += `?${query}`;
}
return truncated;
}
}
---
title: Add 'download' button to Performance Bar
merge_request: 20205
author: Will Chandler
type: changed
......@@ -19,6 +19,7 @@ It allows you to see (from left to right):
- a link to add a request's details to the performance bar; the request can be
added by its full URL (authenticated as the current user), or by the value of
its `X-Request-Id` header
- a link to download the raw JSON used to generate the Performance Bar reports
On the far right is a request selector that allows you to view the same metrics
(excluding the page timing and line profiler) for any requests made while the
......
......@@ -12224,6 +12224,9 @@ msgstr ""
msgid "Performance optimization"
msgstr ""
msgid "PerformanceBar|Download"
msgstr ""
msgid "PerformanceBar|Gitaly calls"
msgstr ""
......
......@@ -4,23 +4,9 @@ import { shallowMount } from '@vue/test-utils';
describe('request selector', () => {
const requests = [
{
id: '123',
url: 'https://gitlab.com/',
hasWarnings: false,
},
{
id: '456',
url: 'https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/1',
hasWarnings: false,
},
{
id: '789',
url: 'https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/1.json?serializer=widget',
hasWarnings: false,
},
{
id: 'abc',
id: 'warningReq',
url: 'https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/1/discussions.json',
truncatedUrl: 'discussions.json',
hasWarnings: true,
},
];
......@@ -28,35 +14,16 @@ describe('request selector', () => {
const wrapper = shallowMount(RequestSelector, {
propsData: {
requests,
currentRequest: requests[1],
currentRequest: requests[0],
},
});
function optionText(requestId) {
return wrapper
.find(`[value='${requestId}']`)
.text()
.trim();
}
it('displays the last component of the path', () => {
expect(optionText(requests[2].id)).toEqual('1.json?serializer=widget');
});
it('keeps the last two components of the path when the last component is numeric', () => {
expect(optionText(requests[1].id)).toEqual('merge_requests/1');
});
it('ignores trailing slashes', () => {
expect(optionText(requests[0].id)).toEqual('gitlab.com');
});
it('has a warning icon if any requests have warnings', () => {
expect(wrapper.find('span > gl-emoji').element.dataset.name).toEqual('warning');
});
it('adds a warning glyph to requests with warnings', () => {
const requestValue = wrapper.find('[value="abc"]').text();
const requestValue = wrapper.find('[value="warningReq"]').text();
expect(requestValue).toContain('discussions.json');
expect(requestValue).toContain('(!)');
......
import PerformanceBarStore from '~/performance_bar/stores/performance_bar_store';
describe('PerformanceBarStore', () => {
describe('truncateUrl', () => {
let store;
const findUrl = id => store.findRequest(id).truncatedUrl;
beforeEach(() => {
store = new PerformanceBarStore();
});
it('ignores trailing slashes', () => {
store.addRequest('id', 'https://gitlab.com/');
expect(findUrl('id')).toEqual('gitlab.com');
});
it('keeps the last two components of the path when the last component is numeric', () => {
store.addRequest('id', 'https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/1');
expect(findUrl('id')).toEqual('merge_requests/1');
});
it('uses the last component of the path', () => {
store.addRequest(
'id',
'https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/1.json?serializer=widget',
);
expect(findUrl('id')).toEqual('1.json?serializer=widget');
});
it('keeps query components', () => {
store.addRequest('id', 'http://localhost:3001/h5bp/html5-boilerplate/?param');
expect(findUrl('id')).toEqual('html5-boilerplate?param');
});
it('keeps components when query contains a slash', () => {
store.addRequest('id', 'http://localhost:3001/h5bp/html5-boilerplate?trunc/ated');
expect(findUrl('id')).toEqual('html5-boilerplate?trunc/ated');
});
it('ignores fragments', () => {
store.addRequest('id', 'http://localhost:3001/h5bp/html5-boilerplate/#frag/ment');
expect(findUrl('id')).toEqual('html5-boilerplate');
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment