Commit c17100cf authored by Phil Hughes's avatar Phil Hughes

Merge branch '33751-track-usage-and-adoption-of-conan-repository' into 'master'

Add UI events tracking to Conan adoption

See merge request gitlab-org/gitlab!21908
parents 9e71803b 51b98110
...@@ -9,6 +9,7 @@ import { ...@@ -9,6 +9,7 @@ import {
GlTable, GlTable,
} from '@gitlab/ui'; } from '@gitlab/ui';
import _ from 'underscore'; import _ from 'underscore';
import Tracking from '~/tracking';
import PackageInformation from './information.vue'; import PackageInformation from './information.vue';
import NpmInstallation from './npm_installation.vue'; import NpmInstallation from './npm_installation.vue';
import MavenInstallation from './maven_installation.vue'; import MavenInstallation from './maven_installation.vue';
...@@ -17,7 +18,8 @@ import { numberToHumanSize } from '~/lib/utils/number_utils'; ...@@ -17,7 +18,8 @@ import { numberToHumanSize } from '~/lib/utils/number_utils';
import timeagoMixin from '~/vue_shared/mixins/timeago'; import timeagoMixin from '~/vue_shared/mixins/timeago';
import { generatePackageInfo } from '../utils'; import { generatePackageInfo } from '../utils';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import { PackageType } from '../constants'; import { PackageType, TrackingActions } from '../../shared/constants';
import { packageTypeToTrackCategory } from '../../shared/utils';
export default { export default {
name: 'PackagesApp', name: 'PackagesApp',
...@@ -36,7 +38,8 @@ export default { ...@@ -36,7 +38,8 @@ export default {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
GlModal: GlModalDirective, GlModal: GlModalDirective,
}, },
mixins: [timeagoMixin], mixins: [timeagoMixin, Tracking.mixin()],
trackingActions: { ...TrackingActions },
props: { props: {
packageEntity: { packageEntity: {
type: Object, type: Object,
...@@ -145,6 +148,11 @@ export default { ...@@ -145,6 +148,11 @@ export default {
created: x.created_at, created: x.created_at,
})); }));
}, },
tracking() {
return {
category: packageTypeToTrackCategory(this.packageEntity.package_type),
};
},
}, },
methods: { methods: {
formatSize(size) { formatSize(size) {
...@@ -233,9 +241,13 @@ export default { ...@@ -233,9 +241,13 @@ export default {
> >
<template #name="items"> <template #name="items">
<icon name="doc-code" class="space-right" /> <icon name="doc-code" class="space-right" />
<gl-link :href="items.item.downloadPath" class="js-file-download">{{ <gl-link
items.item.name :href="items.item.downloadPath"
}}</gl-link> class="js-file-download"
@click="track($options.trackingActions.PULL_PACKAGE)"
>
{{ items.item.name }}
</gl-link>
</template> </template>
<template #created="items"> <template #created="items">
...@@ -253,10 +265,12 @@ export default { ...@@ -253,10 +265,12 @@ export default {
<div class="float-right"> <div class="float-right">
<gl-button @click="cancelDelete()">{{ __('Cancel') }}</gl-button> <gl-button @click="cancelDelete()">{{ __('Cancel') }}</gl-button>
<gl-button <gl-button
ref="modal-delete-button"
data-method="delete" data-method="delete"
:to="destroyPath" :to="destroyPath"
variant="danger" variant="danger"
data-qa-selector="delete_modal_button" data-qa-selector="delete_modal_button"
@click="track($options.trackingActions.DELETE_PACKAGE)"
>{{ __('Delete') }}</gl-button >{{ __('Delete') }}</gl-button
> >
</div> </div>
......
export const PackageType = {
CONAN: 'conan',
MAVEN: 'maven',
NPM: 'npm',
};
export const TrackingLabels = { export const TrackingLabels = {
CODE_INSTRUCTION: 'code_instruction', CODE_INSTRUCTION: 'code_instruction',
MAVEN_INSTALLATION: 'maven_installation', MAVEN_INSTALLATION: 'maven_installation',
......
import { __ } from '~/locale'; import { __ } from '~/locale';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import { PackageType, TrackingActions } from './constants'; import { TrackingActions } from './constants';
import { PackageType } from '../shared/constants';
export const trackInstallationTabChange = { export const trackInstallationTabChange = {
methods: { methods: {
......
<script> <script>
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import { GlTable, GlPagination, GlButton, GlSorting, GlSortingItem, GlModal } from '@gitlab/ui'; import { GlTable, GlPagination, GlButton, GlSorting, GlSortingItem, GlModal } from '@gitlab/ui';
import Tracking from '~/tracking';
import { s__, sprintf } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { s__, sprintf } from '~/locale';
import { import {
LIST_KEY_NAME, LIST_KEY_NAME,
LIST_KEY_PROJECT, LIST_KEY_PROJECT,
...@@ -18,6 +19,8 @@ import { ...@@ -18,6 +19,8 @@ import {
LIST_LABEL_CREATED_AT, LIST_LABEL_CREATED_AT,
LIST_LABEL_ACTIONS, LIST_LABEL_ACTIONS,
} from '../constants'; } from '../constants';
import { TrackingActions } from '../../shared/constants';
import { packageTypeToTrackCategory } from '../../shared/utils';
export default { export default {
components: { components: {
...@@ -30,6 +33,7 @@ export default { ...@@ -30,6 +33,7 @@ export default {
GlModal, GlModal,
Icon, Icon,
}, },
mixins: [Tracking.mixin()],
data() { data() {
return { return {
modalId: 'confirm-delete-pacakge', modalId: 'confirm-delete-pacakge',
...@@ -126,6 +130,14 @@ export default { ...@@ -126,6 +130,14 @@ export default {
false, false,
); );
}, },
tracking() {
const category = this.itemToBeDeleted
? packageTypeToTrackCategory(this.itemToBeDeleted.package_type)
: undefined;
return {
category,
};
},
}, },
methods: { methods: {
onDirectionChange() { onDirectionChange() {
...@@ -140,6 +152,7 @@ export default { ...@@ -140,6 +152,7 @@ export default {
}, },
deleteItemConfirmation() { deleteItemConfirmation() {
this.$emit('package:delete', this.itemToBeDeleted.id); this.$emit('package:delete', this.itemToBeDeleted.id);
this.track(TrackingActions.DELETE_PACKAGE);
this.itemToBeDeleted = null; this.itemToBeDeleted = null;
}, },
deleteItemCanceled() { deleteItemCanceled() {
......
export const PackageType = {
MAVEN: 'maven',
NPM: 'npm',
CONAN: 'conan',
};
export const TrackingActions = {
DELETE_PACKAGE: 'delete_package',
PULL_PACKAGE: 'pull_package',
};
export const TrackingCategories = {
[PackageType.MAVEN]: 'MavenPackages',
[PackageType.NPM]: 'NpmPackages',
[PackageType.CONAN]: 'ConanPackages',
};
import { TrackingCategories } from './constants';
// eslint-disable-next-line import/prefer-default-export
export const packageTypeToTrackCategory = type =>
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
`UI::${TrackingCategories[type]}`;
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { GlModal } from '@gitlab/ui'; import { GlModal } from '@gitlab/ui';
import Tracking from '~/tracking';
import PackagesApp from 'ee/packages/details/components/app.vue'; import PackagesApp from 'ee/packages/details/components/app.vue';
import PackageInformation from 'ee/packages/details/components/information.vue'; import PackageInformation from 'ee/packages/details/components/information.vue';
import NpmInstallation from 'ee/packages/details/components/npm_installation.vue'; import NpmInstallation from 'ee/packages/details/components/npm_installation.vue';
import MavenInstallation from 'ee/packages/details/components/maven_installation.vue'; import MavenInstallation from 'ee/packages/details/components/maven_installation.vue';
import { mavenPackage, mavenFiles, npmPackage, npmFiles } from '../../mock_data'; import * as SharedUtils from 'ee/packages/shared/utils';
import { TrackingActions } from 'ee/packages/shared/constants';
import { mavenPackage, mavenFiles, npmPackage, npmFiles, conanPackage } from '../../mock_data';
describe('PackagesApp', () => { describe('PackagesApp', () => {
let wrapper; let wrapper;
...@@ -44,6 +47,7 @@ describe('PackagesApp', () => { ...@@ -44,6 +47,7 @@ describe('PackagesApp', () => {
const firstFileDownloadLink = () => wrapper.find('.js-file-download'); const firstFileDownloadLink = () => wrapper.find('.js-file-download');
const deleteButton = () => wrapper.find('.js-delete-button'); const deleteButton = () => wrapper.find('.js-delete-button');
const deleteModal = () => wrapper.find(GlModal); const deleteModal = () => wrapper.find(GlModal);
const modalDeleteButton = () => wrapper.find({ ref: 'modal-delete-button' });
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -136,4 +140,44 @@ describe('PackagesApp', () => { ...@@ -136,4 +140,44 @@ describe('PackagesApp', () => {
expect(deleteModal()).toExist(); expect(deleteModal()).toExist();
}); });
}); });
describe('tracking', () => {
let eventSpy;
let utilSpy;
const category = 'foo';
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
utilSpy = jest.spyOn(SharedUtils, 'packageTypeToTrackCategory').mockReturnValue(category);
});
it('tracking category calls packageTypeToTrackCategory', () => {
createComponent({ packageEntity: conanPackage });
expect(wrapper.vm.tracking.category).toBe(category);
expect(utilSpy).toHaveBeenCalledWith('conan');
});
it(`delete button on delete modal call event with ${TrackingActions.DELETE_PACKAGE}`, () => {
createComponent({ packageEntity: conanPackage, canDelete: true, destroyPath: 'foo' });
deleteButton().trigger('click');
return wrapper.vm.$nextTick().then(() => {
modalDeleteButton().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(
category,
TrackingActions.DELETE_PACKAGE,
expect.any(Object),
);
});
});
it(`file download link call event with ${TrackingActions.PULL_PACKAGE}`, () => {
createComponent({ packageEntity: conanPackage });
firstFileDownloadLink().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(
category,
TrackingActions.PULL_PACKAGE,
expect.any(Object),
);
});
});
}); });
import Vue from 'vue'; import Vue from 'vue';
import _ from 'underscore'; import _ from 'underscore';
import Tracking from '~/tracking';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import PackagesList from 'ee/packages/list/components/packages_list.vue'; import PackagesList from 'ee/packages/list/components/packages_list.vue';
import * as SharedUtils from 'ee/packages/shared/utils';
import { TrackingActions } from 'ee/packages/shared/constants';
import stubChildren from 'helpers/stub_children'; import stubChildren from 'helpers/stub_children';
import { packageList } from '../../mock_data'; import { packageList } from '../../mock_data';
...@@ -126,6 +129,7 @@ describe('packages_list', () => { ...@@ -126,6 +129,7 @@ describe('packages_list', () => {
wrapper.vm.deleteItemConfirmation(); wrapper.vm.deleteItemConfirmation();
expect(wrapper.vm.itemToBeDeleted).toEqual(null); expect(wrapper.vm.itemToBeDeleted).toEqual(null);
}); });
it('deleteItemConfirmation emit package:delete', () => { it('deleteItemConfirmation emit package:delete', () => {
wrapper.setData({ itemToBeDeleted: { id: 2 } }); wrapper.setData({ itemToBeDeleted: { id: 2 } });
wrapper.vm.deleteItemConfirmation(); wrapper.vm.deleteItemConfirmation();
...@@ -179,4 +183,30 @@ describe('packages_list', () => { ...@@ -179,4 +183,30 @@ describe('packages_list', () => {
expect(table.classes()).toContain('b-table-stacked-md'); expect(table.classes()).toContain('b-table-stacked-md');
}); });
}); });
describe('tracking', () => {
let eventSpy;
let utilSpy;
const category = 'foo';
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
utilSpy = jest.spyOn(SharedUtils, 'packageTypeToTrackCategory').mockReturnValue(category);
wrapper.setData({ itemToBeDeleted: { package_type: 'conan' } });
});
it('tracking category calls packageTypeToTrackCategory', () => {
expect(wrapper.vm.tracking.category).toBe(category);
expect(utilSpy).toHaveBeenCalledWith('conan');
});
it('deleteItemConfirmation calls event', () => {
wrapper.vm.deleteItemConfirmation();
expect(eventSpy).toHaveBeenCalledWith(
category,
TrackingActions.DELETE_PACKAGE,
expect.any(Object),
);
});
});
}); });
...@@ -50,8 +50,6 @@ export const npmFiles = [ ...@@ -50,8 +50,6 @@ export const npmFiles = [
}, },
]; ];
export const packageList = [mavenPackage, npmPackage];
export const conanPackage = { export const conanPackage = {
conan_metadatum: { conan_metadatum: {
package_channel: 'stable', package_channel: 'stable',
...@@ -67,3 +65,5 @@ export const conanPackage = { ...@@ -67,3 +65,5 @@ export const conanPackage = {
updated_at: '', updated_at: '',
version: '1.0.0', version: '1.0.0',
}; };
export const packageList = [mavenPackage, npmPackage, conanPackage];
import { packageTypeToTrackCategory } from 'ee/packages/shared/utils';
import { PackageType, TrackingCategories } from 'ee/packages/shared/constants';
describe('Packages shared utils', () => {
describe('packageTypeToTrackCategory', () => {
it('prepend UI to package category', () => {
expect(packageTypeToTrackCategory()).toMatchInlineSnapshot(`"UI::undefined"`);
});
it.each(Object.keys(PackageType))('returns a correct category string for %s', packageKey => {
const packageName = PackageType[packageKey];
expect(packageTypeToTrackCategory(packageName)).toBe(
`UI::${TrackingCategories[packageName]}`,
);
});
});
});
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