Commit 17ac753e authored by Nick Kipling's avatar Nick Kipling

Add UI tracking for package install instructions

Update code_installation to support tracking
Add tracking to npm and maven tabs
Add tracking for npm and maven commands
Add tests to cover new tracking
Update snapshot
Added changelog entry

Removed setting of category to rely on default
Removed category const
Updated tests
Removed empty object in track method
Removed manual event binding
Removed unncessary ref
parent 3eb67d94
---
title: Added event tracking to the package details installation components
merge_request: 20967
author:
type: changed
......@@ -17,7 +17,7 @@ import { numberToHumanSize } from '~/lib/utils/number_utils';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { formatDate } from '~/lib/utils/datetime_utility';
import { __, s__, sprintf } from '~/locale';
import PackageType from '../constants';
import { PackageType } from '../constants';
export default {
name: 'PackagesApp',
......
<script>
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import Tracking from '~/tracking';
import { TrackingLabels } from '../constants';
export default {
name: 'CodeInstruction',
components: {
ClipboardButton,
},
mixins: [
Tracking.mixin({
label: TrackingLabels.CODE_INSTRUCTION,
}),
],
props: {
instruction: {
type: String,
......@@ -20,19 +27,37 @@ export default {
required: false,
default: false,
},
trackingAction: {
type: String,
required: false,
default: '',
},
},
methods: {
trackCopy() {
if (this.trackingAction) {
this.track(this.trackingAction);
}
},
},
};
</script>
<template>
<div v-if="!multiline" class="input-group append-bottom-10">
<input :value="instruction" type="text" class="form-control monospace" readonly />
<span class="input-group-append">
<input
:value="instruction"
type="text"
class="form-control monospace js-instruction-input"
readonly
@copy="trackCopy"
/>
<span class="input-group-append js-instruction-button" @click="trackCopy">
<clipboard-button :text="instruction" :title="copyText" class="input-group-text" />
</span>
</div>
<div v-else>
<pre>{{ instruction }}</pre>
<pre class="js-instruction-pre" @copy="trackCopy">{{ instruction }}</pre>
</div>
</template>
......@@ -2,6 +2,8 @@
import { GlTab, GlTabs } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import CodeInstruction from './code_instruction.vue';
import Tracking from '~/tracking';
import { TrackingActions, TrackingLabels } from '../constants';
export default {
name: 'MavenInstallation',
......@@ -10,6 +12,11 @@ export default {
GlTab,
GlTabs,
},
mixins: [
Tracking.mixin({
label: TrackingLabels.MAVEN_INSTALLATION,
}),
],
props: {
heading: {
type: String,
......@@ -110,13 +117,20 @@ export default {
false,
),
},
methods: {
onTabChanged(tabIndex) {
const action = tabIndex === 0 ? TrackingActions.INSTALLATION : TrackingActions.REGISTRY_SETUP;
this.track(action);
},
},
trackingActions: { ...TrackingActions },
};
</script>
<template>
<div class="append-bottom-default">
<gl-tabs>
<gl-tab :title="s__('PackageRegistry|Installation')">
<gl-tabs @input="onTabChanged">
<gl-tab :title="s__('PackageRegistry|Installation')" title-item-class="js-installation-tab">
<div class="prepend-left-default append-right-default">
<p class="prepend-top-8 font-weight-bold">{{ s__('PackageRegistry|Maven XML') }}</p>
<p v-html="$options.i18n.xmlText"></p>
......@@ -125,6 +139,7 @@ export default {
:copy-text="s__('PackageRegistry|Copy Maven XML')"
class="js-maven-xml"
multiline
:tracking-action="$options.trackingActions.COPY_MAVEN_XML"
/>
<p class="prepend-top-default font-weight-bold">
......@@ -134,10 +149,11 @@ export default {
:instruction="mavenCommand"
:copy-text="s__('PackageRegistry|Copy Maven command')"
class="js-maven-command"
:tracking-action="$options.trackingActions.COPY_MAVEN_COMMAND"
/>
</div>
</gl-tab>
<gl-tab :title="s__('PackageRegistry|Registry Setup')">
<gl-tab :title="s__('PackageRegistry|Registry Setup')" title-item-class="js-setup-tab">
<div class="prepend-left-default append-right-default">
<p v-html="$options.i18n.setupText"></p>
<code-instruction
......@@ -145,6 +161,7 @@ export default {
:copy-text="s__('PackageRegistry|Copy Maven registry XML')"
class="js-maven-setup-xml"
multiline
:tracking-action="$options.trackingActions.COPY_MAVEN_SETUP"
/>
<p v-html="helpText"></p>
</div>
......
......@@ -2,6 +2,8 @@
import { GlTab, GlTabs } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import CodeInstruction from './code_instruction.vue';
import Tracking from '~/tracking';
import { TrackingActions, TrackingLabels } from '../constants';
export default {
name: 'NpmInstallation',
......@@ -10,6 +12,11 @@ export default {
GlTab,
GlTabs,
},
mixins: [
Tracking.mixin({
label: TrackingLabels.NPM_INSTALLATION,
}),
],
props: {
name: {
type: String,
......@@ -63,19 +70,27 @@ export default {
);
},
},
methods: {
onTabChanged(tabIndex) {
const action = tabIndex === 0 ? TrackingActions.INSTALLATION : TrackingActions.REGISTRY_SETUP;
this.track(action);
},
},
trackingActions: { ...TrackingActions },
};
</script>
<template>
<div class="append-bottom-default">
<gl-tabs>
<gl-tab :title="s__('PackageRegistry|Installation')">
<gl-tabs @input="onTabChanged">
<gl-tab :title="s__('PackageRegistry|Installation')" title-item-class="js-installation-tab">
<div class="prepend-left-default append-right-default">
<p class="prepend-top-8 font-weight-bold">{{ s__('PackageRegistry|npm') }}</p>
<code-instruction
:instruction="npmCommand"
:copy-text="s__('PackageRegistry|Copy npm command')"
class="js-npm-install"
:tracking-action="$options.trackingActions.COPY_NPM_INSTALL_COMMAND"
/>
<p class="prepend-top-default font-weight-bold">{{ s__('PackageRegistry|yarn') }}</p>
......@@ -83,16 +98,18 @@ export default {
:instruction="yarnCommand"
:copy-text="s__('PackageRegistry|Copy yarn command')"
class="js-yarn-install"
:tracking-action="$options.trackingActions.COPY_YARN_INSTALL_COMMAND"
/>
</div>
</gl-tab>
<gl-tab :title="s__('PackageRegistry|Registry Setup')">
<gl-tab :title="s__('PackageRegistry|Registry Setup')" title-item-class="js-setup-tab">
<div class="prepend-left-default append-right-default">
<p class="prepend-top-8 font-weight-bold">{{ s__('PackageRegistry|npm') }}</p>
<code-instruction
:instruction="npmSetupCommand"
:copy-text="s__('PackageRegistry|Copy npm setup command')"
class="js-npm-setup"
:tracking-action="$options.trackingActions.COPY_NPM_SETUP_COMMAND"
/>
<p class="prepend-top-default font-weight-bold">{{ s__('PackageRegistry|yarn') }}</p>
......@@ -100,6 +117,7 @@ export default {
:instruction="yarnSetupCommand"
:copy-text="s__('PackageRegistry|Copy yarn setup command')"
class="js-yarn-setup"
:tracking-action="$options.trackingActions.COPY_YARN_SETUP_COMMAND"
/>
<p v-html="helpText"></p>
......
const PackageType = {
export const PackageType = {
MAVEN: 'maven',
NPM: 'npm',
};
export default PackageType;
export const TrackingLabels = {
CODE_INSTRUCTION: 'code_instruction',
MAVEN_INSTALLATION: 'maven_installation',
NPM_INSTALLATION: 'npm_installation',
};
export const TrackingActions = {
INSTALLATION: 'installation',
REGISTRY_SETUP: 'registry_setup',
COPY_MAVEN_XML: 'copy_maven_xml',
COPY_MAVEN_COMMAND: 'copy_maven_command',
COPY_MAVEN_SETUP: 'copy_maven_setup_xml',
COPY_NPM_INSTALL_COMMAND: 'copy_npm_install_command',
COPY_NPM_SETUP_COMMAND: 'copy_npm_setup_command',
COPY_YARN_INSTALL_COMMAND: 'copy_yarn_install_command',
COPY_YARN_SETUP_COMMAND: 'copy_yarn_setup_command',
};
......@@ -2,7 +2,9 @@
exports[`Package code instruction multiline to match the snapshot 1`] = `
<div>
<pre>
<pre
class="js-instruction-pre"
>
this is some
multiline text
</pre>
......@@ -14,13 +16,13 @@ exports[`Package code instruction single line to match the default snapshot 1`]
class="input-group append-bottom-10"
>
<input
class="form-control monospace"
class="form-control monospace js-instruction-input"
readonly="readonly"
type="text"
/>
<span
class="input-group-append"
class="input-group-append js-instruction-button"
>
<button
class="btn input-group-text btn-secondary btn-default"
......
import { mount } from '@vue/test-utils';
import CodeInstruction from 'ee/packages/details/components/code_instruction.vue';
import { TrackingLabels } from 'ee/packages/details/constants';
import Tracking from '~/tracking';
describe('Package code instruction', () => {
let wrapper;
......@@ -18,6 +20,10 @@ describe('Package code instruction', () => {
});
}
const findInstructionInput = () => wrapper.find('.js-instruction-input');
const findInstructionPre = () => wrapper.find('.js-instruction-pre');
const findInstructionButton = () => wrapper.find('.js-instruction-button');
afterEach(() => {
wrapper.destroy();
});
......@@ -43,4 +49,62 @@ describe('Package code instruction', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
describe('tracking', () => {
let eventSpy;
const trackingAction = 'test_action';
const label = TrackingLabels.CODE_INSTRUCTION;
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
});
it('should not track when no trackingAction is provided', () => {
createComponent();
findInstructionButton().trigger('click');
expect(eventSpy).toHaveBeenCalledTimes(0);
});
describe('when trackingAction is provided for single line', () => {
beforeEach(() =>
createComponent({
trackingAction,
}),
);
it('should track when copying from the input', () => {
findInstructionInput().trigger('copy');
expect(eventSpy).toHaveBeenCalledWith(undefined, trackingAction, {
label,
});
});
it('should track when the copy button is pressed', () => {
findInstructionButton().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, trackingAction, {
label,
});
});
});
describe('when trackingAction is provided for multiline', () => {
beforeEach(() =>
createComponent({
trackingAction,
multiline: true,
}),
);
it('should track when copying from the multiline pre element', () => {
findInstructionPre().trigger('copy');
expect(eventSpy).toHaveBeenCalledWith(undefined, trackingAction, {
label,
});
});
});
});
});
import { mount } from '@vue/test-utils';
import MavenInstallation from 'ee/packages/details/components/maven_installation.vue';
import { TrackingActions, TrackingLabels } from 'ee/packages/details/constants';
import {
generateMavenCommand,
generateXmlCodeBlock,
......@@ -7,6 +8,7 @@ import {
mavenMetadata,
registryUrl,
} from '../mock_data';
import Tracking from '~/tracking';
describe('MavenInstallation', () => {
let wrapper;
......@@ -21,6 +23,8 @@ describe('MavenInstallation', () => {
const xmlCodeBlock = generateXmlCodeBlock(mavenMetadata);
const mavenSetupXml = generateMavenSetupXml();
const installationTab = () => wrapper.find('.js-installation-tab > a');
const setupTab = () => wrapper.find('.js-setup-tab > a');
const xmlCode = () => wrapper.find('.js-maven-xml > pre');
const mavenCommand = () => wrapper.find('.js-maven-command > input');
const xmlSetup = () => wrapper.find('.js-maven-setup-xml > pre');
......@@ -79,4 +83,30 @@ describe('MavenInstallation', () => {
expect(xmlSetup().text()).toBe(mavenSetupXml);
});
});
describe('tab change tracking', () => {
let eventSpy;
const label = TrackingLabels.MAVEN_INSTALLATION;
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
});
it('should track when the setup tab is clicked', () => {
setupTab().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.REGISTRY_SETUP, {
label,
});
});
it('should track when the installation tab is clicked', () => {
setupTab().trigger('click');
installationTab().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.INSTALLATION, {
label,
});
});
});
});
import { mount } from '@vue/test-utils';
import NpmInstallation from 'ee/packages/details/components/npm_installation.vue';
import { TrackingActions, TrackingLabels } from 'ee/packages/details/constants';
import Tracking from '~/tracking';
describe('NpmInstallation', () => {
let wrapper;
......@@ -20,6 +22,8 @@ describe('NpmInstallation', () => {
const yarnInstall = `yarn add ${packageScopeName}`;
const yarnSetup = `echo \\"${packageScope}:registry\\" \\"${registryUrl}\\" >> .yarnrc`;
const installationTab = () => wrapper.find('.js-installation-tab > a');
const setupTab = () => wrapper.find('.js-setup-tab > a');
const installCommand = type => wrapper.find(`.js-${type}-install > input`);
const setupCommand = type => wrapper.find(`.js-${type}-setup > input`);
......@@ -73,4 +77,31 @@ describe('NpmInstallation', () => {
expect(setupCommand('yarn').element.value).toBe(yarnSetup);
});
});
describe('tab change tracking', () => {
let eventSpy;
const label = TrackingLabels.NPM_INSTALLATION;
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
createComponent();
});
it('should track when the setup tab is clicked', () => {
setupTab().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.REGISTRY_SETUP, {
label,
});
});
it('should track when the installation tab is clicked', () => {
setupTab().trigger('click');
installationTab().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.INSTALLATION, {
label,
});
});
});
});
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