Commit eb01d7be authored by anna_vovchenko's avatar anna_vovchenko Committed by Ezekiel Kigbo

Added Snowplow events to the Clusters page

The events are added for:
- changing the tab
- Agent registration modal process

Changelog: added
parent fec794ba
<script> <script>
import { GlTabs, GlTab } from '@gitlab/ui'; import { GlTabs, GlTab } from '@gitlab/ui';
import { CLUSTERS_TABS, MAX_CLUSTERS_LIST, MAX_LIST_COUNT, AGENT } from '../constants'; import Tracking from '~/tracking';
import {
CLUSTERS_TABS,
MAX_CLUSTERS_LIST,
MAX_LIST_COUNT,
AGENT,
EVENT_LABEL_TABS,
EVENT_ACTIONS,
} from '../constants';
import Agents from './agents.vue'; import Agents from './agents.vue';
import InstallAgentModal from './install_agent_modal.vue'; import InstallAgentModal from './install_agent_modal.vue';
import ClustersActions from './clusters_actions.vue'; import ClustersActions from './clusters_actions.vue';
import Clusters from './clusters.vue'; import Clusters from './clusters.vue';
import ClustersViewAll from './clusters_view_all.vue'; import ClustersViewAll from './clusters_view_all.vue';
const trackingMixin = Tracking.mixin({ label: EVENT_LABEL_TABS });
export default { export default {
components: { components: {
GlTabs, GlTabs,
...@@ -18,6 +28,7 @@ export default { ...@@ -18,6 +28,7 @@ export default {
InstallAgentModal, InstallAgentModal,
}, },
CLUSTERS_TABS, CLUSTERS_TABS,
mixins: [trackingMixin],
props: { props: {
defaultBranchName: { defaultBranchName: {
default: '.noBranch', default: '.noBranch',
...@@ -34,9 +45,12 @@ export default { ...@@ -34,9 +45,12 @@ export default {
methods: { methods: {
onTabChange(tabName) { onTabChange(tabName) {
this.selectedTabIndex = CLUSTERS_TABS.findIndex((tab) => tab.queryParamValue === tabName); this.selectedTabIndex = CLUSTERS_TABS.findIndex((tab) => tab.queryParamValue === tabName);
this.maxAgents = tabName === AGENT ? MAX_LIST_COUNT : MAX_CLUSTERS_LIST; this.maxAgents = tabName === AGENT ? MAX_LIST_COUNT : MAX_CLUSTERS_LIST;
}, },
trackTabChange(tab) {
const tabName = CLUSTERS_TABS[tab].queryParamValue;
this.track(EVENT_ACTIONS.change_tab, { property: tabName });
},
}, },
}; };
</script> </script>
...@@ -47,6 +61,7 @@ export default { ...@@ -47,6 +61,7 @@ export default {
sync-active-tab-with-query-params sync-active-tab-with-query-params
nav-class="gl-flex-grow-1 gl-align-items-center" nav-class="gl-flex-grow-1 gl-align-items-center"
lazy lazy
@input="trackTabChange"
> >
<gl-tab <gl-tab
v-for="(tab, idx) in $options.CLUSTERS_TABS" v-for="(tab, idx) in $options.CLUSTERS_TABS"
......
...@@ -11,8 +11,15 @@ import { ...@@ -11,8 +11,15 @@ import {
import { helpPagePath } from '~/helpers/help_page_helper'; import { helpPagePath } from '~/helpers/help_page_helper';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import CodeBlock from '~/vue_shared/components/code_block.vue'; import CodeBlock from '~/vue_shared/components/code_block.vue';
import Tracking from '~/tracking';
import { generateAgentRegistrationCommand } from '../clusters_util'; import { generateAgentRegistrationCommand } from '../clusters_util';
import { INSTALL_AGENT_MODAL_ID, I18N_AGENT_MODAL, KAS_DISABLED_ERROR } from '../constants'; import {
INSTALL_AGENT_MODAL_ID,
I18N_AGENT_MODAL,
KAS_DISABLED_ERROR,
EVENT_LABEL_MODAL,
EVENT_ACTIONS,
} from '../constants';
import { addAgentToStore, addAgentConfigToStore } from '../graphql/cache_update'; import { addAgentToStore, addAgentConfigToStore } from '../graphql/cache_update';
import createAgent from '../graphql/mutations/create_agent.mutation.graphql'; import createAgent from '../graphql/mutations/create_agent.mutation.graphql';
import createAgentToken from '../graphql/mutations/create_agent_token.mutation.graphql'; import createAgentToken from '../graphql/mutations/create_agent_token.mutation.graphql';
...@@ -20,8 +27,12 @@ import getAgentsQuery from '../graphql/queries/get_agents.query.graphql'; ...@@ -20,8 +27,12 @@ import getAgentsQuery from '../graphql/queries/get_agents.query.graphql';
import agentConfigurations from '../graphql/queries/agent_configurations.query.graphql'; import agentConfigurations from '../graphql/queries/agent_configurations.query.graphql';
import AvailableAgentsDropdown from './available_agents_dropdown.vue'; import AvailableAgentsDropdown from './available_agents_dropdown.vue';
const trackingMixin = Tracking.mixin({ label: EVENT_LABEL_MODAL });
export default { export default {
modalId: INSTALL_AGENT_MODAL_ID, modalId: INSTALL_AGENT_MODAL_ID,
EVENT_ACTIONS,
EVENT_LABEL_MODAL,
components: { components: {
AvailableAgentsDropdown, AvailableAgentsDropdown,
ClipboardButton, ClipboardButton,
...@@ -34,6 +45,7 @@ export default { ...@@ -34,6 +45,7 @@ export default {
GlModal, GlModal,
GlSprintf, GlSprintf,
}, },
mixins: [trackingMixin],
inject: ['projectPath', 'kasAddress', 'emptyStateImage'], inject: ['projectPath', 'kasAddress', 'emptyStateImage'],
props: { props: {
defaultBranchName: { defaultBranchName: {
...@@ -81,7 +93,7 @@ export default { ...@@ -81,7 +93,7 @@ export default {
return !this.registering && this.agentName !== null; return !this.registering && this.agentName !== null;
}, },
canCancel() { canCancel() {
return !this.registered && !this.registering && this.isRegisterModal; return !this.registered && !this.registering && this.isAgentRegistrationModal;
}, },
agentRegistrationCommand() { agentRegistrationCommand() {
return generateAgentRegistrationCommand(this.agentToken, this.kasAddress); return generateAgentRegistrationCommand(this.agentToken, this.kasAddress);
...@@ -117,21 +129,24 @@ export default { ...@@ -117,21 +129,24 @@ export default {
return `/${this.projectPath}`; return `/${this.projectPath}`;
}, },
modalType() { modalType() {
return !this.availableAgents?.length && !this.registered ? 'install' : 'register'; return !this.availableAgents?.length && !this.registered
? 'empty_state'
: 'agent_registration';
}, },
modalSize() { modalSize() {
return this.isInstallModal ? 'sm' : 'md'; return this.isEmptyStateModal ? 'sm' : 'md';
}, },
isInstallModal() { isEmptyStateModal() {
return this.modalType === 'install'; return this.modalType === 'empty_state';
}, },
isRegisterModal() { isAgentRegistrationModal() {
return this.modalType === 'register'; return this.modalType === 'agent_registration';
}, },
}, },
methods: { methods: {
setAgentName(name) { setAgentName(name) {
this.agentName = name; this.agentName = name;
this.track(EVENT_ACTIONS.select);
}, },
closeModal() { closeModal() {
this.$refs.modal.hide(); this.$refs.modal.hide();
...@@ -242,8 +257,9 @@ export default { ...@@ -242,8 +257,9 @@ export default {
static static
lazy lazy
@hidden="resetModal" @hidden="resetModal"
@show="track($options.EVENT_ACTIONS.open, { property: modalType })"
> >
<template v-if="isRegisterModal"> <template v-if="isAgentRegistrationModal">
<template v-if="!registered"> <template v-if="!registered">
<p> <p>
<strong>{{ i18n.selectAgentTitle }}</strong> <strong>{{ i18n.selectAgentTitle }}</strong>
...@@ -347,23 +363,40 @@ export default { ...@@ -347,23 +363,40 @@ export default {
</template> </template>
<template #modal-footer> <template #modal-footer>
<gl-button v-if="canCancel" @click="closeModal">{{ i18n.cancel }} </gl-button> <gl-button
v-if="canCancel"
:data-track-action="$options.EVENT_ACTIONS.click"
:data-track-label="$options.EVENT_LABEL_MODAL"
data-track-property="cancel"
@click="closeModal"
>{{ i18n.cancel }}
</gl-button>
<gl-button v-if="registered" variant="confirm" category="primary" @click="closeModal" <gl-button
v-if="registered"
variant="confirm"
category="primary"
:data-track-action="$options.EVENT_ACTIONS.click"
:data-track-label="$options.EVENT_LABEL_MODAL"
data-track-property="close"
@click="closeModal"
>{{ i18n.close }} >{{ i18n.close }}
</gl-button> </gl-button>
<gl-button <gl-button
v-else-if="isRegisterModal" v-else-if="isAgentRegistrationModal"
:disabled="!nextButtonDisabled" :disabled="!nextButtonDisabled"
variant="confirm" variant="confirm"
category="primary" category="primary"
:data-track-action="$options.EVENT_ACTIONS.click"
:data-track-label="$options.EVENT_LABEL_MODAL"
data-track-property="register"
@click="registerAgent" @click="registerAgent"
>{{ i18n.registerAgentButton }} >{{ i18n.registerAgentButton }}
</gl-button> </gl-button>
<gl-button <gl-button
v-if="isInstallModal" v-if="isEmptyStateModal"
:href="repositoryPath" :href="repositoryPath"
variant="confirm" variant="confirm"
category="secondary" category="secondary"
...@@ -371,7 +404,14 @@ export default { ...@@ -371,7 +404,14 @@ export default {
>{{ i18n.secondaryButton }} >{{ i18n.secondaryButton }}
</gl-button> </gl-button>
<gl-button v-if="isInstallModal" variant="confirm" category="primary" @click="closeModal" <gl-button
v-if="isEmptyStateModal"
variant="confirm"
category="primary"
:data-track-action="$options.EVENT_ACTIONS.click"
:data-track-label="$options.EVENT_LABEL_MODAL"
data-track-property="done"
@click="closeModal"
>{{ i18n.done }} >{{ i18n.done }}
</gl-button> </gl-button>
</template> </template>
......
...@@ -65,7 +65,7 @@ export const STATUSES = { ...@@ -65,7 +65,7 @@ export const STATUSES = {
}; };
export const I18N_AGENT_MODAL = { export const I18N_AGENT_MODAL = {
register: { agent_registration: {
registerAgentButton: s__('ClusterAgents|Register Agent'), registerAgentButton: s__('ClusterAgents|Register Agent'),
close: __('Close'), close: __('Close'),
cancel: __('Cancel'), cancel: __('Cancel'),
...@@ -104,7 +104,7 @@ export const I18N_AGENT_MODAL = { ...@@ -104,7 +104,7 @@ export const I18N_AGENT_MODAL = {
registrationErrorTitle: __('Failed to register Agent'), registrationErrorTitle: __('Failed to register Agent'),
unknownError: s__('ClusterAgents|An unknown error occurred. Please try again.'), unknownError: s__('ClusterAgents|An unknown error occurred. Please try again.'),
}, },
install: { empty_state: {
modalTitle: s__('ClusterAgents|Install new Agent'), modalTitle: s__('ClusterAgents|Install new Agent'),
modalBody: s__( modalBody: s__(
'ClusterAgents|To install an Agent you should create an agent directory in the Repository first. We recommend that you add the Agent configuration to the directory before you start the installation process.', 'ClusterAgents|To install an Agent you should create an agent directory in the Repository first. We recommend that you add the Agent configuration to the directory before you start the installation process.',
...@@ -236,3 +236,12 @@ export const CLUSTERS_ACTIONS = { ...@@ -236,3 +236,12 @@ export const CLUSTERS_ACTIONS = {
export const AGENT = 'agent'; export const AGENT = 'agent';
export const CERTIFICATE_BASED = 'certificate_based'; export const CERTIFICATE_BASED = 'certificate_based';
export const EVENT_LABEL_MODAL = 'agent_registration_modal';
export const EVENT_LABEL_TABS = 'kubernetes_section_tabs';
export const EVENT_ACTIONS = {
open: 'open_modal',
select: 'select_agent',
click: 'click_button',
change_tab: 'change_tab',
};
import { GlTabs, GlTab } from '@gitlab/ui'; import { GlTabs, GlTab } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { mockTracking } from 'helpers/tracking_helper';
import ClustersMainView from '~/clusters_list/components/clusters_main_view.vue'; import ClustersMainView from '~/clusters_list/components/clusters_main_view.vue';
import InstallAgentModal from '~/clusters_list/components/install_agent_modal.vue'; import InstallAgentModal from '~/clusters_list/components/install_agent_modal.vue';
import { import {
...@@ -8,12 +9,15 @@ import { ...@@ -8,12 +9,15 @@ import {
CLUSTERS_TABS, CLUSTERS_TABS,
MAX_CLUSTERS_LIST, MAX_CLUSTERS_LIST,
MAX_LIST_COUNT, MAX_LIST_COUNT,
EVENT_LABEL_TABS,
EVENT_ACTIONS,
} from '~/clusters_list/constants'; } from '~/clusters_list/constants';
const defaultBranchName = 'default-branch'; const defaultBranchName = 'default-branch';
describe('ClustersMainViewComponent', () => { describe('ClustersMainViewComponent', () => {
let wrapper; let wrapper;
let trackingSpy;
const propsData = { const propsData = {
defaultBranchName, defaultBranchName,
...@@ -23,6 +27,7 @@ describe('ClustersMainViewComponent', () => { ...@@ -23,6 +27,7 @@ describe('ClustersMainViewComponent', () => {
wrapper = shallowMountExtended(ClustersMainView, { wrapper = shallowMountExtended(ClustersMainView, {
propsData, propsData,
}); });
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
}); });
afterEach(() => { afterEach(() => {
...@@ -71,6 +76,7 @@ describe('ClustersMainViewComponent', () => { ...@@ -71,6 +76,7 @@ describe('ClustersMainViewComponent', () => {
beforeEach(() => { beforeEach(() => {
findComponent().vm.$emit('changeTab', AGENT); findComponent().vm.$emit('changeTab', AGENT);
}); });
it('changes the tab', () => { it('changes the tab', () => {
expect(findTabs().attributes('value')).toBe('1'); expect(findTabs().attributes('value')).toBe('1');
}); });
...@@ -78,5 +84,13 @@ describe('ClustersMainViewComponent', () => { ...@@ -78,5 +84,13 @@ describe('ClustersMainViewComponent', () => {
it('passes correct max-agents param to the modal', () => { it('passes correct max-agents param to the modal', () => {
expect(findModal().props('maxAgents')).toBe(MAX_LIST_COUNT); expect(findModal().props('maxAgents')).toBe(MAX_LIST_COUNT);
}); });
it('sends the correct tracking event', () => {
findTabs().vm.$emit('input', 1);
expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTIONS.change_tab, {
label: EVENT_LABEL_TABS,
property: AGENT,
});
});
}); });
}); });
...@@ -2,9 +2,15 @@ import { GlAlert, GlButton, GlFormInputGroup } from '@gitlab/ui'; ...@@ -2,9 +2,15 @@ import { GlAlert, GlButton, GlFormInputGroup } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils'; import { createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { mockTracking } from 'helpers/tracking_helper';
import AvailableAgentsDropdown from '~/clusters_list/components/available_agents_dropdown.vue'; import AvailableAgentsDropdown from '~/clusters_list/components/available_agents_dropdown.vue';
import InstallAgentModal from '~/clusters_list/components/install_agent_modal.vue'; import InstallAgentModal from '~/clusters_list/components/install_agent_modal.vue';
import { I18N_AGENT_MODAL, MAX_LIST_COUNT } from '~/clusters_list/constants'; import {
I18N_AGENT_MODAL,
MAX_LIST_COUNT,
EVENT_LABEL_MODAL,
EVENT_ACTIONS,
} from '~/clusters_list/constants';
import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql'; import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql';
import getAgentConfigurations from '~/clusters_list/graphql/queries/agent_configurations.query.graphql'; import getAgentConfigurations from '~/clusters_list/graphql/queries/agent_configurations.query.graphql';
import createAgentMutation from '~/clusters_list/graphql/mutations/create_agent.mutation.graphql'; import createAgentMutation from '~/clusters_list/graphql/mutations/create_agent.mutation.graphql';
...@@ -34,6 +40,7 @@ const maxAgents = MAX_LIST_COUNT; ...@@ -34,6 +40,7 @@ const maxAgents = MAX_LIST_COUNT;
describe('InstallAgentModal', () => { describe('InstallAgentModal', () => {
let wrapper; let wrapper;
let apolloProvider; let apolloProvider;
let trackingSpy;
const configurations = [{ agentName: 'agent-name' }]; const configurations = [{ agentName: 'agent-name' }];
const apolloQueryResponse = { const apolloQueryResponse = {
...@@ -56,7 +63,7 @@ describe('InstallAgentModal', () => { ...@@ -56,7 +63,7 @@ describe('InstallAgentModal', () => {
const findActionButton = () => findButtonByVariant('confirm'); const findActionButton = () => findButtonByVariant('confirm');
const findCancelButton = () => findButtonByVariant('default'); const findCancelButton = () => findButtonByVariant('default');
const findSecondaryButton = () => wrapper.findByTestId('agent-secondary-button'); const findSecondaryButton = () => wrapper.findByTestId('agent-secondary-button');
const findImage = () => wrapper.findByRole('img', { alt: I18N_AGENT_MODAL.install.altText }); const findImage = () => wrapper.findByRole('img', { alt: I18N_AGENT_MODAL.empty_state.altText });
const expectDisabledAttribute = (element, disabled) => { const expectDisabledAttribute = (element, disabled) => {
if (disabled) { if (disabled) {
...@@ -121,6 +128,7 @@ describe('InstallAgentModal', () => { ...@@ -121,6 +128,7 @@ describe('InstallAgentModal', () => {
[getAgentConfigurations, jest.fn().mockResolvedValue(apolloQueryResponse)], [getAgentConfigurations, jest.fn().mockResolvedValue(apolloQueryResponse)],
]); ]);
createWrapper(); createWrapper();
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
}); });
afterEach(() => { afterEach(() => {
...@@ -129,7 +137,7 @@ describe('InstallAgentModal', () => { ...@@ -129,7 +137,7 @@ describe('InstallAgentModal', () => {
}); });
describe('when agent configurations are present', () => { describe('when agent configurations are present', () => {
const i18n = I18N_AGENT_MODAL.register; const i18n = I18N_AGENT_MODAL.agent_registration;
describe('initial state', () => { describe('initial state', () => {
it('renders the dropdown for available agents', () => { it('renders the dropdown for available agents', () => {
...@@ -150,6 +158,14 @@ describe('InstallAgentModal', () => { ...@@ -150,6 +158,14 @@ describe('InstallAgentModal', () => {
expect(findActionButton().text()).toBe(i18n.registerAgentButton); expect(findActionButton().text()).toBe(i18n.registerAgentButton);
expectDisabledAttribute(findActionButton(), true); expectDisabledAttribute(findActionButton(), true);
}); });
it('sends the event with the modalType', () => {
findModal().vm.$emit('show');
expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTIONS.open, {
label: EVENT_LABEL_MODAL,
property: 'agent_registration',
});
});
}); });
describe('an agent is selected', () => { describe('an agent is selected', () => {
...@@ -161,6 +177,12 @@ describe('InstallAgentModal', () => { ...@@ -161,6 +177,12 @@ describe('InstallAgentModal', () => {
expect(findActionButton().isVisible()).toBe(true); expect(findActionButton().isVisible()).toBe(true);
expectDisabledAttribute(findActionButton(), false); expectDisabledAttribute(findActionButton(), false);
}); });
it('sends the correct tracking event', () => {
expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTIONS.select, {
label: EVENT_LABEL_MODAL,
});
});
}); });
describe('registering an agent', () => { describe('registering an agent', () => {
...@@ -247,7 +269,7 @@ describe('InstallAgentModal', () => { ...@@ -247,7 +269,7 @@ describe('InstallAgentModal', () => {
}); });
describe('when there are no agent configurations present', () => { describe('when there are no agent configurations present', () => {
const i18n = I18N_AGENT_MODAL.install; const i18n = I18N_AGENT_MODAL.empty_state;
const apolloQueryEmptyResponse = { const apolloQueryEmptyResponse = {
data: { data: {
project: { project: {
...@@ -272,5 +294,13 @@ describe('InstallAgentModal', () => { ...@@ -272,5 +294,13 @@ describe('InstallAgentModal', () => {
expect(findSecondaryButton().isVisible()).toBe(true); expect(findSecondaryButton().isVisible()).toBe(true);
expect(findSecondaryButton().text()).toBe(i18n.secondaryButton); expect(findSecondaryButton().text()).toBe(i18n.secondaryButton);
}); });
it('sends the event with the modalType', () => {
findModal().vm.$emit('show');
expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTIONS.open, {
label: EVENT_LABEL_MODAL,
property: 'empty_state',
});
});
}); });
}); });
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