Commit ecc5634f authored by David Pisek's avatar David Pisek Committed by Savas Vedova

Only track sec training urls load once

This change makes sure that the tracking of loading security
training urls only happens once.
parent f32d23cc
...@@ -22,6 +22,8 @@ export const i18n = { ...@@ -22,6 +22,8 @@ export const i18n = {
loading: __('Loading'), loading: __('Loading'),
}; };
export const TRAINING_URL_POLLING_INTERVAL = 5000;
export default { export default {
i18n, i18n,
TEMP_PROVIDER_LOGOS, TEMP_PROVIDER_LOGOS,
...@@ -61,7 +63,7 @@ export default { ...@@ -61,7 +63,7 @@ export default {
}, },
securityTrainingUrls: { securityTrainingUrls: {
query: securityTrainingVulnerabilityQuery, query: securityTrainingVulnerabilityQuery,
pollInterval: 5000, pollInterval: TRAINING_URL_POLLING_INTERVAL,
update({ project }) { update({ project }) {
if (!project) { if (!project) {
return []; return [];
...@@ -119,15 +121,7 @@ export default { ...@@ -119,15 +121,7 @@ export default {
return this.supportedIdentifiersExternalIds.length > 0; return this.supportedIdentifiersExternalIds.length > 0;
}, },
hasSecurityTrainingUrls() { hasSecurityTrainingUrls() {
const hasSecurityTrainingUrls = this.securityTrainingUrls?.length > 0; return this.securityTrainingUrls?.length > 0;
if (hasSecurityTrainingUrls) {
this.track(TRACK_TRAINING_LOADED_ACTION, {
property: this.projectFullPath,
});
}
return hasSecurityTrainingUrls;
}, },
}, },
watch: { watch: {
...@@ -138,6 +132,17 @@ export default { ...@@ -138,6 +132,17 @@ export default {
}, },
}, },
}, },
created() {
const unwatchHasSecurityTrainingUrls = this.$watch('hasSecurityTrainingUrls', (hasUrls) => {
if (hasUrls) {
this.track(TRACK_TRAINING_LOADED_ACTION, {
property: this.projectFullPath,
});
// we only want to track this once, so we immediately unsubscribe after the first track
unwatchHasSecurityTrainingUrls();
}
});
},
methods: { methods: {
clickTrainingLink(name) { clickTrainingLink(name) {
this.track(TRACK_CLICK_TRAINING_LINK_ACTION, { this.track(TRACK_CLICK_TRAINING_LINK_ACTION, {
......
...@@ -5,6 +5,7 @@ import { GlLink, GlIcon, GlSkeletonLoader } from '@gitlab/ui'; ...@@ -5,6 +5,7 @@ import { GlLink, GlIcon, GlSkeletonLoader } from '@gitlab/ui';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import VulnerabilityTraining, { import VulnerabilityTraining, {
i18n, i18n,
TRAINING_URL_POLLING_INTERVAL,
} from 'ee/vulnerabilities/components/vulnerability_training.vue'; } from 'ee/vulnerabilities/components/vulnerability_training.vue';
import securityTrainingProvidersQuery from '~/security_configuration/graphql/security_training_providers.query.graphql'; import securityTrainingProvidersQuery from '~/security_configuration/graphql/security_training_providers.query.graphql';
import securityTrainingVulnerabilityQuery from '~/security_configuration/graphql/security_training_vulnerability.query.graphql'; import securityTrainingVulnerabilityQuery from '~/security_configuration/graphql/security_training_vulnerability.query.graphql';
...@@ -29,15 +30,6 @@ import { getSecurityTrainingProjectData, testIdentifiers } from './mock_data'; ...@@ -29,15 +30,6 @@ import { getSecurityTrainingProjectData, testIdentifiers } from './mock_data';
Vue.use(VueApollo); Vue.use(VueApollo);
const createTrainingData = (first = {}, second = {}, urls) =>
getSecurityTrainingProjectData({
urls,
urlOverrides: {
first,
second,
},
});
const projectFullPath = 'namespace/project'; const projectFullPath = 'namespace/project';
const TEST_TRAINING_PROVIDERS_ALL_DISABLED = getSecurityTrainingProvidersData(); const TEST_TRAINING_PROVIDERS_ALL_DISABLED = getSecurityTrainingProvidersData();
...@@ -45,8 +37,8 @@ const TEST_TRAINING_PROVIDERS_FIRST_ENABLED = getSecurityTrainingProvidersData({ ...@@ -45,8 +37,8 @@ const TEST_TRAINING_PROVIDERS_FIRST_ENABLED = getSecurityTrainingProvidersData({
providerOverrides: { first: { isEnabled: true } }, providerOverrides: { first: { isEnabled: true } },
}); });
const TEST_TRAINING_PROVIDERS_DEFAULT = TEST_TRAINING_PROVIDERS_FIRST_ENABLED; const TEST_TRAINING_PROVIDERS_DEFAULT = TEST_TRAINING_PROVIDERS_FIRST_ENABLED;
const TEST_TRAINING_VULNERABILITY_DEFAULT = getSecurityTrainingProjectData(); const TEST_TRAINING_URLS_DEFAULT = getSecurityTrainingProjectData();
const TEST_TRAINING_VULNERABILITY_NO_URLS = createTrainingData(null, null, []); const TEST_TRAINING_URLS_EMPTY = getSecurityTrainingProjectData({ urls: [] });
describe('VulnerabilityTraining component', () => { describe('VulnerabilityTraining component', () => {
let wrapper; let wrapper;
...@@ -61,8 +53,7 @@ describe('VulnerabilityTraining component', () => { ...@@ -61,8 +53,7 @@ describe('VulnerabilityTraining component', () => {
], ],
[ [
securityTrainingVulnerabilityQuery, securityTrainingVulnerabilityQuery,
projectQueryHandler || projectQueryHandler || jest.fn().mockResolvedValue(TEST_TRAINING_URLS_DEFAULT.response),
jest.fn().mockResolvedValue(TEST_TRAINING_VULNERABILITY_DEFAULT.response),
], ],
]); ]);
}; };
...@@ -152,9 +143,7 @@ describe('VulnerabilityTraining component', () => { ...@@ -152,9 +143,7 @@ describe('VulnerabilityTraining component', () => {
it('displays message when there are no security training urls', async () => { it('displays message when there are no security training urls', async () => {
createApolloProvider({ createApolloProvider({
projectQueryHandler: jest projectQueryHandler: jest.fn().mockResolvedValue(TEST_TRAINING_URLS_EMPTY.response),
.fn()
.mockResolvedValue(TEST_TRAINING_VULNERABILITY_NO_URLS.response),
}); });
createComponent(); createComponent();
await waitForQueryToBeLoaded(); await waitForQueryToBeLoaded();
...@@ -179,8 +168,12 @@ describe('VulnerabilityTraining component', () => { ...@@ -179,8 +168,12 @@ describe('VulnerabilityTraining component', () => {
it('displays when there are supported identifiers and some urls are in pending status', async () => { it('displays when there are supported identifiers and some urls are in pending status', async () => {
createApolloProvider({ createApolloProvider({
projectQueryHandler: jest.fn().mockResolvedValue( projectQueryHandler: jest.fn().mockResolvedValue(
createTrainingData({ getSecurityTrainingProjectData({
urlOverrides: {
first: {
status: SECURITY_TRAINING_URL_STATUS_PENDING, status: SECURITY_TRAINING_URL_STATUS_PENDING,
},
},
}).response, }).response,
), ),
}); });
...@@ -200,8 +193,8 @@ describe('VulnerabilityTraining component', () => { ...@@ -200,8 +193,8 @@ describe('VulnerabilityTraining component', () => {
apolloQuery = wrapper.vm.$apollo.queries.securityTrainingUrls; apolloQuery = wrapper.vm.$apollo.queries.securityTrainingUrls;
}); });
it('sets polling at 5000 ms', () => { it(`sets polling at ${TRAINING_URL_POLLING_INTERVAL} ms`, () => {
expect(apolloQuery.options.pollInterval).toBe(5000); expect(apolloQuery.options.pollInterval).toBe(TRAINING_URL_POLLING_INTERVAL);
}); });
it('stops polling when every training url status is completed', async () => { it('stops polling when every training url status is completed', async () => {
...@@ -260,9 +253,7 @@ describe('VulnerabilityTraining component', () => { ...@@ -260,9 +253,7 @@ describe('VulnerabilityTraining component', () => {
it('does not display training item if there are no securityTrainingUrls', async () => { it('does not display training item if there are no securityTrainingUrls', async () => {
createApolloProvider({ createApolloProvider({
projectQueryHandler: jest projectQueryHandler: jest.fn().mockResolvedValue(TEST_TRAINING_URLS_EMPTY.response),
.fn()
.mockResolvedValue(TEST_TRAINING_VULNERABILITY_NO_URLS.response),
}); });
createComponent(); createComponent();
await waitForQueryToBeLoaded(); await waitForQueryToBeLoaded();
...@@ -305,20 +296,39 @@ describe('VulnerabilityTraining component', () => { ...@@ -305,20 +296,39 @@ describe('VulnerabilityTraining component', () => {
property: projectFullPath, property: projectFullPath,
}); });
it('tracks when the training link is loading', async () => { it('tracks when the training urls are first loaded', async () => {
createApolloProvider({ jest.useFakeTimers();
projectQueryHandler: jest.fn().mockResolvedValue(
createTrainingData({ const projectQueryHandler = jest.fn().mockResolvedValue(
getSecurityTrainingProjectData({
urlOverrides: {
first: {
status: SECURITY_TRAINING_URL_STATUS_PENDING, status: SECURITY_TRAINING_URL_STATUS_PENDING,
},
},
}).response, }).response,
), );
expect(trackingSpy).not.toHaveBeenCalled();
createApolloProvider({
projectQueryHandler,
}); });
createComponent(); createComponent();
await waitForQueryToBeLoaded(); await waitForQueryToBeLoaded();
// after the first loading of the urls, the tracking should be called
expect(trackingSpy).toHaveBeenCalledWith(undefined, TRACK_TRAINING_LOADED_ACTION, { expect(trackingSpy).toHaveBeenCalledWith(undefined, TRACK_TRAINING_LOADED_ACTION, {
property: projectFullPath, property: projectFullPath,
}); });
// fake a poll-cycle
jest.advanceTimersByTime(TRAINING_URL_POLLING_INTERVAL);
await waitForQueryToBeLoaded();
// make sure that we queried twice
expect(projectQueryHandler).toHaveBeenCalledTimes(2);
expect(trackingSpy).toHaveBeenCalledTimes(1);
}); });
it.each([0, 1])('tracks when training link %s gets clicked', async (index) => { it.each([0, 1])('tracks when training link %s gets clicked', async (index) => {
......
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