diff --git a/CHANGELOG.md b/CHANGELOG.md
index 56f59a4..66153a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Added
- Anonymization of video recordings controllable from Gravity
+- Option to enable/disable video recording anonymization in debug mode
- Track new HTML events:
- `contextmenu`
- `dblclick`
diff --git a/README.md b/README.md
index 12bd4bd..42e143e 100644
--- a/README.md
+++ b/README.md
@@ -63,24 +63,25 @@ GravityCollector.init(/*options*/)
The `GravityCollector.init()` can take a `CollectorOptions` object with the following optional properties:
-| key | type | use | default value |
-| ---------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
-| authKey | String | The authentication key provided by Gravity to select the correct collection | |
-| requestInterval | Integer | Time (in ms) between two sends to Gravity server (buffering) | 1000 |
-| gravityServerUrl | String | Gravity server URL | https://api.gravity.smartesting.com |
-| debug | Boolean | Logs user action in the console instead of sending them to Gravity | false |
-| maxDelay | Integer | In debug mode, adds a random delay (in ms) between 0 and this value before printing an user action. | 500 |
-| excludeRegex | RegExp | Deprecated, use selectorsOptions
instead.
Regular expression of ID and class names to ignore in selector computation. | null |
-| customSelector | String (optional) | Deprecated, use selectorsOptions
instead.
The attribute to use as a selector if defined on an HTML element targeted by a user action. | undefined |
-| selectorsOptions | Object (optional) | See [Work with selectors](#work-with-selectors). | undefined |
-| sessionsPercentageKept | [0..100] | Rate of sessions to be collected | 100 |
-| rejectSession | function `() => boolean` | Boolean function to ignore session tracking. For instance, to ignore sessions from some bots:
rejectSession: () => /bot|googlebot|robot/i.test(navigator.userAgent)
| `() => false` |
-| onPublish | function (optional) | Adds a function called after each publish to the gravity server. | undefined |
-| originsToRecord | String[] (optional) | Deprecated, renamed recordRequestsFor
. | undefined |
-| recordRequestsFor | String[] (optional) | The Gravity Data Collector does not record requests by default. You must specify here the URL origin(s) of the requests to record. For example: "https://myserver.com/" | undefined |
-| buildId | String (optional) | The build reference when running tests | undefined |
-| enableEventRecording | Boolean (optional) | Set to `false` to deactivate any recording (event & video) | true |
-| enableVideoRecording | Boolean (optional) | Set to `false` to deactivate video recording | true |
+| key | type | use | default value |
+| ------------------------ | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
+| authKey | String | The authentication key provided by Gravity to select the correct collection | |
+| requestInterval | Integer | Time (in ms) between two sends to Gravity server (buffering) | 1000 |
+| gravityServerUrl | String | Gravity server URL | https://api.gravity.smartesting.com |
+| debug | Boolean | Logs user action in the console instead of sending them to Gravity | false |
+| maxDelay | Integer | In debug mode, adds a random delay (in ms) between 0 and this value before printing an user action. | 500 |
+| excludeRegex | RegExp | Deprecated, use selectorsOptions
instead.
Regular expression of ID and class names to ignore in selector computation. | null |
+| customSelector | String (optional) | Deprecated, use selectorsOptions
instead.
The attribute to use as a selector if defined on an HTML element targeted by a user action. | undefined |
+| selectorsOptions | Object (optional) | See [Work with selectors](#work-with-selectors). | undefined |
+| sessionsPercentageKept | [0..100] | Rate of sessions to be collected | 100 |
+| rejectSession | function `() => boolean` | Boolean function to ignore session tracking. For instance, to ignore sessions from some bots:
rejectSession: () => /bot|googlebot|robot/i.test(navigator.userAgent)
| `() => false` |
+| onPublish | function (optional) | Adds a function called after each publish to the gravity server. | undefined |
+| originsToRecord | String[] (optional) | Deprecated, renamed recordRequestsFor
. | undefined |
+| recordRequestsFor | String[] (optional) | The Gravity Data Collector does not record requests by default. You must specify here the URL origin(s) of the requests to record. For example: "https://myserver.com/" | undefined |
+| buildId | String (optional) | The build reference when running tests | undefined |
+| enableEventRecording | Boolean (optional) | Set to `false` to deactivate any recording (event & video) (only in debug mode, otherwise this option is controlled from the Gravity interface) | true |
+| enableVideoRecording | Boolean (optional) | Set to `false` to deactivate video recording (only in debug mode, otherwise this option is controlled from the Gravity interface) | true |
+| enableVideoAnonymization | Boolean (optional) | Set to `false` to deactivate video anonymization (only in debug mode, otherwise this option is controlled from the Gravity interface) | true |
## Features
diff --git a/src/collector/CollectorWrapper.ts b/src/collector/CollectorWrapper.ts
index ee7dcab..a173adf 100644
--- a/src/collector/CollectorWrapper.ts
+++ b/src/collector/CollectorWrapper.ts
@@ -260,9 +260,9 @@ class CollectorWrapper {
private async fetchRecordingSettings(): Promise {
if (this.options.debug) {
return {
- enableEventRecording: true,
- enableVideoRecording: true,
- enableVideoAnonymization: false,
+ enableEventRecording: this.options.enableEventRecording,
+ enableVideoRecording: this.options.enableVideoRecording,
+ enableVideoAnonymization: this.options.enableVideoAnonymization,
}
}
@@ -270,11 +270,10 @@ class CollectorWrapper {
if (settings === null || error !== null) {
return ALL_RECORDING_SETTINGS_DISABLED
}
- const enableEventRecording = settings.sessionRecording ?? this.options.enableEventRecording
- const enableVideoRecording = settings.videoRecording ?? this.options.enableVideoRecording
+
return {
- enableEventRecording,
- enableVideoRecording,
+ enableEventRecording: settings.sessionRecording,
+ enableVideoRecording: settings.videoRecording,
enableVideoAnonymization: settings.videoAnonymization,
}
})
diff --git a/src/collector/sessionRecording.test.ts b/src/collector/sessionRecording.test.ts
index 3e0701d..b29fae1 100644
--- a/src/collector/sessionRecording.test.ts
+++ b/src/collector/sessionRecording.test.ts
@@ -65,7 +65,60 @@ describe('Session recording (events & video) depends on remote Gravity settings'
beforeEach(() => installSpies(ConsoleGravityClient))
afterEach(uninstallSpies)
- it('use default settings', async () => {
+ describe('use options settings if available', () => {
+ it('records events & video', async () => {
+ const collector = installer()
+ .withOptions({
+ enableEventRecording: true,
+ enableVideoRecording: true,
+ })
+ .install()
+
+ await emitEachEventKind(collector)
+ expect(handleSessionUserActions).toHaveBeenCalled()
+ expect(handleSessionTraits).toHaveBeenCalled()
+ expect(handleScreenRecords).toHaveBeenCalled()
+
+ expect(terminateEventRecording).not.toHaveBeenCalled()
+ expect(terminateVideoRecording).not.toHaveBeenCalled()
+ })
+
+ it('records events BUT no videos', async () => {
+ const collector = installer()
+ .withOptions({
+ enableEventRecording: true,
+ enableVideoRecording: false,
+ })
+ .install()
+
+ await emitEachEventKind(collector)
+ expect(handleSessionUserActions).toHaveBeenCalled()
+ expect(handleSessionTraits).toHaveBeenCalled()
+ expect(handleScreenRecords).not.toHaveBeenCalled()
+
+ expect(terminateEventRecording).not.toHaveBeenCalled()
+ expect(terminateVideoRecording).toHaveBeenCalled()
+ })
+
+ it('records nothing', async () => {
+ const collector = installer()
+ .withOptions({
+ enableEventRecording: false,
+ enableVideoRecording: true,
+ })
+ .install()
+
+ await emitEachEventKind(collector)
+ expect(handleSessionUserActions).not.toHaveBeenCalled()
+ expect(handleSessionTraits).not.toHaveBeenCalled()
+ expect(handleScreenRecords).not.toHaveBeenCalled()
+
+ expect(terminateEventRecording).toHaveBeenCalled()
+ expect(terminateVideoRecording).toHaveBeenCalled()
+ })
+ })
+
+ it('else use default settings', async () => {
const collector = installer().install()
await emitEachEventKind(collector)
diff --git a/src/gravity-client/AbstractGravityClient.ts b/src/gravity-client/AbstractGravityClient.ts
index f2c86ff..b66930a 100644
--- a/src/gravity-client/AbstractGravityClient.ts
+++ b/src/gravity-client/AbstractGravityClient.ts
@@ -32,9 +32,10 @@ export interface GravityClientOptions {
onPublish?: (userActions: ReadonlyArray) => void
}
-export type RecordingSettings = Pick & {
- enableVideoAnonymization: boolean
-}
+export type RecordingSettings = Pick<
+ CollectorOptions,
+ 'enableEventRecording' | 'enableVideoRecording' | 'enableVideoAnonymization'
+>
export default abstract class AbstractGravityClient implements IGravityClient {
private readonly sessionUserActionBuffer: DataBuffering
diff --git a/src/types.ts b/src/types.ts
index 2c1e6db..9e139d5 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -268,6 +268,7 @@ export interface CollectorOptions {
disableVideoRecording?: boolean
enableEventRecording?: boolean
enableVideoRecording?: boolean
+ enableVideoAnonymization?: boolean
}
export type CollectorOptionsWithWindow = CollectorOptions & {
diff --git a/src/utils/completeOptions.test.ts b/src/utils/completeOptions.test.ts
index 88052fd..c8c2295 100644
--- a/src/utils/completeOptions.test.ts
+++ b/src/utils/completeOptions.test.ts
@@ -13,15 +13,24 @@ describe('completeOptions', () => {
})
it('throws an error when provided an invalid "sessionsPercentageKept"', () => {
- expect(() => completeOptions({ authKey: '123', sessionsPercentageKept: NaN })).toThrow(
- 'option "sessionsPercentageKept": NaN is not a valid percentage (should be in range 0..100)',
- )
- expect(() => completeOptions({ authKey: '123', sessionsPercentageKept: -1 })).toThrow(
- 'option "sessionsPercentageKept": -1 is not a valid percentage (should be in range 0..100)',
- )
- expect(() => completeOptions({ authKey: '123', sessionsPercentageKept: 101 })).toThrow(
- 'option "sessionsPercentageKept": 101 is not a valid percentage (should be in range 0..100)',
- )
+ expect(() =>
+ completeOptions({
+ authKey: '123',
+ sessionsPercentageKept: NaN,
+ }),
+ ).toThrow('option "sessionsPercentageKept": NaN is not a valid percentage (should be in range 0..100)')
+ expect(() =>
+ completeOptions({
+ authKey: '123',
+ sessionsPercentageKept: -1,
+ }),
+ ).toThrow('option "sessionsPercentageKept": -1 is not a valid percentage (should be in range 0..100)')
+ expect(() =>
+ completeOptions({
+ authKey: '123',
+ sessionsPercentageKept: 101,
+ }),
+ ).toThrow('option "sessionsPercentageKept": 101 is not a valid percentage (should be in range 0..100)')
})
describe('when selectorsOptions is set', () => {
@@ -31,33 +40,50 @@ describe('completeOptions', () => {
it('throws an error when selectorsOptions.attributes is not an array of string', () => {
// @ts-expect-error
selectorsOptions = { attributes: null }
- expect(() => completeOptions({ authKey, selectorsOptions })).toThrow(
- 'option "selectorsOptions.attributes": "null" is not a valid option. Expected a list of strings',
- )
+ expect(() =>
+ completeOptions({
+ authKey,
+ selectorsOptions,
+ }),
+ ).toThrow('option "selectorsOptions.attributes": "null" is not a valid option. Expected a list of strings')
// @ts-expect-error
selectorsOptions = { attributes: 'data-testid' }
- expect(() => completeOptions({ authKey, selectorsOptions })).toThrow(
- 'option "selectorsOptions.attributes": "data-testid" is not a valid option. Expected a list of strings',
- )
+ expect(() =>
+ completeOptions({
+ authKey,
+ selectorsOptions,
+ }),
+ ).toThrow('option "selectorsOptions.attributes": "data-testid" is not a valid option. Expected a list of strings')
// @ts-expect-error
selectorsOptions = { attributes: ['data-testid', null] }
- expect(() => completeOptions({ authKey, selectorsOptions })).toThrow(
- 'option "selectorsOptions.attributes": "null" is not a valid string',
- )
+ expect(() =>
+ completeOptions({
+ authKey,
+ selectorsOptions,
+ }),
+ ).toThrow('option "selectorsOptions.attributes": "null" is not a valid string')
})
it('throws an error when selectorOptions.queries is not a list of QueryType', () => {
// @ts-expect-error
selectorsOptions = { queries: null }
- expect(() => completeOptions({ authKey, selectorsOptions })).toThrow(
- 'option "selectorsOptions.queries": "null" is not a valid option. Expected a list of QueryType',
- )
+ expect(() =>
+ completeOptions({
+ authKey,
+ selectorsOptions,
+ }),
+ ).toThrow('option "selectorsOptions.queries": "null" is not a valid option. Expected a list of QueryType')
// @ts-expect-error
selectorsOptions = { queries: [QueryType.id, 'doupidou'] }
- expect(() => completeOptions({ authKey, selectorsOptions })).toThrow(
+ expect(() =>
+ completeOptions({
+ authKey,
+ selectorsOptions,
+ }),
+ ).toThrow(
'option "selectorsOptions.queries": "doupidou" is not a valid QueryType. Valid values are: id, class, tag',
)
})
@@ -65,13 +91,21 @@ describe('completeOptions', () => {
it('throws an error when selectorOptions.excludedQueries is not a list of QueryType', () => {
// @ts-expect-error
selectorsOptions = { excludedQueries: null }
- expect(() => completeOptions({ authKey, selectorsOptions })).toThrow(
- 'option "selectorsOptions.excludedQueries": "null" is not a valid option. Expected a list of QueryType',
- )
+ expect(() =>
+ completeOptions({
+ authKey,
+ selectorsOptions,
+ }),
+ ).toThrow('option "selectorsOptions.excludedQueries": "null" is not a valid option. Expected a list of QueryType')
// @ts-expect-error
selectorsOptions = { excludedQueries: [QueryType.id, 'doupidou'] }
- expect(() => completeOptions({ authKey, selectorsOptions })).toThrow(
+ expect(() =>
+ completeOptions({
+ authKey,
+ selectorsOptions,
+ }),
+ ).toThrow(
'option "selectorsOptions.excludedQueries": "doupidou" is not a valid QueryType. Valid values are: id, class, tag',
)
})
@@ -81,7 +115,10 @@ describe('completeOptions', () => {
queries: [QueryType.class, QueryType.tag],
attributes: ['role', 'data-testid'],
}
- completeOptions({ authKey, selectorsOptions })
+ completeOptions({
+ authKey,
+ selectorsOptions,
+ })
})
})
@@ -100,6 +137,7 @@ describe('completeOptions', () => {
window,
enableEventRecording: true,
enableVideoRecording: true,
+ enableVideoAnonymization: true,
}
expect(completed).toStrictEqual(expected)
})
@@ -124,6 +162,7 @@ describe('completeOptions', () => {
window,
enableEventRecording: true,
enableVideoRecording: true,
+ enableVideoAnonymization: true,
}
expect(completed).toStrictEqual(expected)
})
@@ -152,6 +191,7 @@ describe('completeOptions', () => {
window,
enableEventRecording: true,
enableVideoRecording: true,
+ enableVideoAnonymization: true,
}
expect(completed).toStrictEqual(expected)
})
@@ -174,6 +214,7 @@ describe('completeOptions', () => {
window,
enableEventRecording: true,
enableVideoRecording: true,
+ enableVideoAnonymization: true,
}
expect(completed).toStrictEqual(expected)
})
@@ -196,6 +237,7 @@ describe('completeOptions', () => {
window,
enableEventRecording: true,
enableVideoRecording: true,
+ enableVideoAnonymization: true,
}
expect(completed).toStrictEqual(expected)
})
@@ -218,6 +260,7 @@ describe('completeOptions', () => {
window,
enableEventRecording: true,
enableVideoRecording: true,
+ enableVideoAnonymization: true,
}
expect(completed).toStrictEqual(expected)
})
@@ -240,6 +283,7 @@ describe('completeOptions', () => {
window,
enableEventRecording: true,
enableVideoRecording: true,
+ enableVideoAnonymization: true,
}
expect(completed).toStrictEqual(expected)
})
@@ -247,16 +291,38 @@ describe('completeOptions', () => {
})
it('use deprecated `disableVideoRecording` to set `enableVideoRecording`', () => {
- expect(completeOptions({ authKey: '', disableVideoRecording: true })).toMatchObject({
+ expect(
+ completeOptions({
+ authKey: '',
+ disableVideoRecording: true,
+ }),
+ ).toMatchObject({
enableVideoRecording: false,
})
- expect(completeOptions({ authKey: '', disableVideoRecording: false })).toMatchObject({
+ expect(
+ completeOptions({
+ authKey: '',
+ disableVideoRecording: false,
+ }),
+ ).toMatchObject({
enableVideoRecording: true,
})
- expect(completeOptions({ authKey: '', disableVideoRecording: true, enableVideoRecording: true })).toMatchObject({
+ expect(
+ completeOptions({
+ authKey: '',
+ disableVideoRecording: true,
+ enableVideoRecording: true,
+ }),
+ ).toMatchObject({
enableVideoRecording: true,
})
- expect(completeOptions({ authKey: '', disableVideoRecording: false, enableVideoRecording: false })).toMatchObject({
+ expect(
+ completeOptions({
+ authKey: '',
+ disableVideoRecording: false,
+ enableVideoRecording: false,
+ }),
+ ).toMatchObject({
enableVideoRecording: false,
})
})
diff --git a/src/utils/completeOptions.ts b/src/utils/completeOptions.ts
index c4df405..eaecd3b 100644
--- a/src/utils/completeOptions.ts
+++ b/src/utils/completeOptions.ts
@@ -22,6 +22,7 @@ export default function completeOptions(options?: Partial): Co
rejectSession: DEFAULT_SESSION_REJECTION,
enableEventRecording: true,
enableVideoRecording: options.disableVideoRecording === undefined ? true : !options.disableVideoRecording,
+ enableVideoAnonymization: true,
}
const debugDefaultOptions = {