Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
24a6b0f5
Commit
24a6b0f5
authored
Apr 13, 2022
by
Axel García
Committed by
Jacques Erasmus
Apr 13, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fire a Snowplow events with its definition on FE
Changelog: added
parent
570d2628
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
132 additions
and
4 deletions
+132
-4
app/assets/javascripts/tracking/tracking.js
app/assets/javascripts/tracking/tracking.js
+66
-4
spec/frontend/tracking/tracking_spec.js
spec/frontend/tracking/tracking_spec.js
+66
-0
No files found.
app/assets/javascripts/tracking/tracking.js
View file @
24a6b0f5
...
@@ -13,8 +13,12 @@ import {
...
@@ -13,8 +13,12 @@ import {
const
ALLOWED_URL_HASHES
=
[
'
#diff
'
,
'
#note
'
];
const
ALLOWED_URL_HASHES
=
[
'
#diff
'
,
'
#note
'
];
export
default
class
Tracking
{
export
default
class
Tracking
{
static
queuedEvents
=
[];
static
nonInitializedQueue
=
[];
static
initialized
=
false
;
static
initialized
=
false
;
static
definitionsLoaded
=
false
;
static
definitionsManifest
=
{};
static
definitionsEventsQueue
=
[];
static
definitions
=
[];
/**
/**
* (Legacy) Determines if tracking is enabled at the user level.
* (Legacy) Determines if tracking is enabled at the user level.
...
@@ -54,13 +58,71 @@ export default class Tracking {
...
@@ -54,13 +58,71 @@ export default class Tracking {
}
}
if
(
!
this
.
initialized
)
{
if
(
!
this
.
initialized
)
{
this
.
queuedEvents
.
push
(
eventData
);
this
.
nonInitializedQueue
.
push
(
eventData
);
return
false
;
return
false
;
}
}
return
dispatchSnowplowEvent
(...
eventData
);
return
dispatchSnowplowEvent
(...
eventData
);
}
}
/**
* Preloads event definitions.
*
* @returns {undefined}
*/
static
loadDefinitions
()
{
// TODO: fetch definitions from the server and flush the queue
// See https://gitlab.com/gitlab-org/gitlab/-/issues/358256
this
.
definitionsLoaded
=
true
;
while
(
this
.
definitionsEventsQueue
.
length
)
{
this
.
dispatchFromDefinition
(...
this
.
definitionsEventsQueue
.
shift
());
}
}
/**
* Dispatches a structured event with data from its event definition.
*
* @param {String} basename
* @param {Object} eventData
* @returns {undefined|Boolean}
*/
static
definition
(
basename
,
eventData
=
{})
{
if
(
!
this
.
enabled
())
{
return
false
;
}
if
(
!
(
basename
in
this
.
definitionsManifest
))
{
throw
new
Error
(
`Missing Snowplow event definition "
${
basename
}
"`
);
}
return
this
.
dispatchFromDefinition
(
basename
,
eventData
);
}
/**
* Builds an event with data from a valid definition and sends it to
* Snowplow. If the definitions are not loaded, it pushes the data to a queue.
*
* @param {String} basename
* @param {Object} eventData
* @returns {undefined|Boolean}
*/
static
dispatchFromDefinition
(
basename
,
eventData
)
{
if
(
!
this
.
definitionsLoaded
)
{
this
.
definitionsEventsQueue
.
push
([
basename
,
eventData
]);
return
false
;
}
const
eventDefinition
=
this
.
definitions
.
find
((
definition
)
=>
definition
.
key
===
basename
);
return
this
.
event
(
eventData
.
category
??
eventDefinition
.
category
,
eventData
.
action
??
eventDefinition
.
action
,
eventData
,
);
}
/**
/**
* Dispatches any event emitted before initialization.
* Dispatches any event emitted before initialization.
*
*
...
@@ -69,8 +131,8 @@ export default class Tracking {
...
@@ -69,8 +131,8 @@ export default class Tracking {
static
flushPendingEvents
()
{
static
flushPendingEvents
()
{
this
.
initialized
=
true
;
this
.
initialized
=
true
;
while
(
this
.
queuedEvents
.
length
)
{
while
(
this
.
nonInitializedQueue
.
length
)
{
dispatchSnowplowEvent
(...
this
.
queuedEvents
.
shift
());
dispatchSnowplowEvent
(...
this
.
nonInitializedQueue
.
shift
());
}
}
}
}
...
...
spec/frontend/tracking/tracking_spec.js
View file @
24a6b0f5
...
@@ -129,6 +129,72 @@ describe('Tracking', () => {
...
@@ -129,6 +129,72 @@ describe('Tracking', () => {
});
});
});
});
describe
(
'
.definition
'
,
()
=>
{
const
TEST_VALID_BASENAME
=
'
202108302307_default_click_button
'
;
const
TEST_EVENT_DATA
=
{
category
:
undefined
,
action
:
'
click_button
'
};
let
eventSpy
;
let
dispatcherSpy
;
beforeAll
(()
=>
{
Tracking
.
definitionsManifest
=
{
'
202108302307_default_click_button
'
:
'
config/events/202108302307_default_click_button.yml
'
,
};
});
beforeEach
(()
=>
{
eventSpy
=
jest
.
spyOn
(
Tracking
,
'
event
'
);
dispatcherSpy
=
jest
.
spyOn
(
Tracking
,
'
dispatchFromDefinition
'
);
});
it
(
'
throws an error if the definition does not exists
'
,
()
=>
{
const
basename
=
'
20220230_default_missing_definition
'
;
const
expectedError
=
new
Error
(
`Missing Snowplow event definition "
${
basename
}
"`
);
expect
(()
=>
Tracking
.
definition
(
basename
)).
toThrow
(
expectedError
);
});
it
(
'
dispatches an event from a definition present in the manifest
'
,
()
=>
{
Tracking
.
definition
(
TEST_VALID_BASENAME
);
expect
(
dispatcherSpy
).
toHaveBeenCalledWith
(
TEST_VALID_BASENAME
,
{});
});
it
(
'
push events to the queue if not loaded
'
,
()
=>
{
Tracking
.
definitionsLoaded
=
false
;
Tracking
.
definitionsEventsQueue
=
[];
const
dispatched
=
Tracking
.
definition
(
TEST_VALID_BASENAME
);
expect
(
dispatched
).
toBe
(
false
);
expect
(
Tracking
.
definitionsEventsQueue
[
0
]).
toStrictEqual
([
TEST_VALID_BASENAME
,
{}]);
expect
(
eventSpy
).
not
.
toHaveBeenCalled
();
});
it
(
'
dispatch events when the definition is loaded
'
,
()
=>
{
const
definition
=
{
key
:
TEST_VALID_BASENAME
,
...
TEST_EVENT_DATA
};
Tracking
.
definitions
=
[{
...
definition
}];
Tracking
.
definitionsEventsQueue
=
[];
Tracking
.
definitionsLoaded
=
true
;
const
dispatched
=
Tracking
.
definition
(
TEST_VALID_BASENAME
);
expect
(
dispatched
).
not
.
toBe
(
false
);
expect
(
Tracking
.
definitionsEventsQueue
).
toEqual
([]);
expect
(
eventSpy
).
toHaveBeenCalledWith
(
definition
.
category
,
definition
.
action
,
{});
});
it
(
'
lets defined event data takes precedence
'
,
()
=>
{
const
definition
=
{
key
:
TEST_VALID_BASENAME
,
category
:
undefined
,
action
:
'
click_button
'
};
const
eventData
=
{
category
:
TEST_CATEGORY
};
Tracking
.
definitions
=
[{
...
definition
}];
Tracking
.
definitionsLoaded
=
true
;
Tracking
.
definition
(
TEST_VALID_BASENAME
,
eventData
);
expect
(
eventSpy
).
toHaveBeenCalledWith
(
TEST_CATEGORY
,
definition
.
action
,
eventData
);
});
});
describe
(
'
.enableFormTracking
'
,
()
=>
{
describe
(
'
.enableFormTracking
'
,
()
=>
{
it
(
'
tells snowplow to enable form tracking, with only explicit contexts
'
,
()
=>
{
it
(
'
tells snowplow to enable form tracking, with only explicit contexts
'
,
()
=>
{
const
config
=
{
forms
:
{
allow
:
[
'
form-class1
'
]
},
fields
:
{
allow
:
[
'
input-class1
'
]
}
};
const
config
=
{
forms
:
{
allow
:
[
'
form-class1
'
]
},
fields
:
{
allow
:
[
'
input-class1
'
]
}
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment