diff --git a/cypress/integration/capture.spec.js b/cypress/integration/capture.spec.js index 10cd39ddb1..c09901710a 100644 --- a/cypress/integration/capture.spec.js +++ b/cypress/integration/capture.spec.js @@ -23,7 +23,11 @@ describe('Event capture', () => { }, }).as('decide') - cy.visit('./playground/cypress') + cy.visit('./playground/cypress', { + onBeforeLoad(win) { + cy.stub(win.console, 'error').as('consoleError') + }, + }) cy.posthogInit(given.options) if (waitForDecide) { cy.wait('@decide') @@ -58,6 +62,16 @@ describe('Event capture', () => { cy.phCaptures().should('include', '$rageclick') }) + it('doesnt capture rage clicks when autocapture is disabled', () => { + given('options', () => ({ rageclick: true, autocapture: false })) + + start() + + cy.get('body').click(100, 100).click(98, 102).click(101, 103) + + cy.phCaptures().should('not.include', '$rageclick') + }) + describe('session recording enabled from API', () => { given('sessionRecording', () => ({ endpoint: '/ses/', @@ -91,6 +105,7 @@ describe('Event capture', () => { cy.wait(50) cy.get('[data-cy-custom-event-button]').click() cy.phCaptures().should('deep.equal', ['$pageview', 'custom-event']) + cy.get('@consoleError').should('not.be.called') }) }) diff --git a/src/__tests__/autocapture.js b/src/__tests__/autocapture.js index 2f3e07ddb6..9e7223ba48 100644 --- a/src/__tests__/autocapture.js +++ b/src/__tests__/autocapture.js @@ -395,6 +395,8 @@ describe('Autocapture system', () => { return 'testtoken' case 'mask_all_element_attributes': return false + case 'autocapture': + return true } }), token: 'testtoken', @@ -435,6 +437,53 @@ describe('Autocapture system', () => { lib.capture.resetHistory() }) + it('should not capture events when get_config returns false, when an element matching any of the event selectors is clicked', () => { + lib = { + _prepare_callback: sandbox.spy((callback) => callback), + get_config: sandbox.spy(function (key) { + switch (key) { + case 'api_host': + return 'https://test.com' + case 'token': + return 'testtoken' + case 'mask_all_element_attributes': + return false + case 'autocapture': + return false + } + }), + token: 'testtoken', + capture: sandbox.spy(), + get_distinct_id() { + return 'distinctid' + }, + toolbar: { + maybeLoadEditor: jest.fn(), + }, + } + autocapture.init(lib) + autocapture.afterDecideResponse(given.decideResponse, lib) + + const eventElement1 = document.createElement('div') + const eventElement2 = document.createElement('div') + const propertyElement = document.createElement('div') + eventElement1.className = 'event-element-1' + eventElement1.style.cursor = 'pointer' + eventElement2.className = 'event-element-2' + eventElement2.style.cursor = 'pointer' + propertyElement.className = 'property-element' + propertyElement.textContent = 'my property value' + document.body.appendChild(eventElement1) + document.body.appendChild(eventElement2) + document.body.appendChild(propertyElement) + + expect(lib.capture.callCount).toBe(0) + simulateClick(eventElement1) + simulateClick(eventElement2) + expect(lib.capture.callCount).toBe(0) + lib.capture.resetHistory() + }) + it('includes necessary metadata as properties when capturing an event', () => { const elTarget = document.createElement('a') elTarget.setAttribute('href', 'http://test.com') @@ -869,6 +918,7 @@ describe('Autocapture system', () => { given('config', () => ({ api_host: 'https://test.com', token: 'testtoken', + autocapture: true, })) given('decideResponse', () => ({ config: { enable_collect_everything: true } })) @@ -880,12 +930,24 @@ describe('Autocapture system', () => { jest.spyOn(autocapture, '_addDomEventHandlers') }) - it('should call _addDomEventHandlders', () => { + it('should call _addDomEventHandlders if autocapture is true', () => { given.subject() expect(autocapture._addDomEventHandlers).toHaveBeenCalled() }) + it('should not call _addDomEventHandlders if autocapture is false', () => { + given('config', () => ({ + api_host: 'https://test.com', + token: 'testtoken', + autocapture: false, + })) + + given.subject() + + expect(autocapture._addDomEventHandlers).not.toHaveBeenCalled() + }) + it('should NOT call _addDomEventHandlders if the decide request fails', () => { given('decideResponse', () => ({ status: 0, error: 'Bad HTTP status: 400 Bad Request' })) @@ -907,7 +969,7 @@ describe('Autocapture system', () => { autocapture.afterDecideResponse(given.decideResponse, given.posthog) expect(autocapture._addDomEventHandlers).toHaveBeenCalledTimes(1) - given('config', () => ({ api_host: 'https://test.com', token: 'anotherproject' })) + given('config', () => ({ api_host: 'https://test.com', token: 'anotherproject', autocapture: true })) autocapture.afterDecideResponse(given.decideResponse, given.posthog) expect(autocapture._addDomEventHandlers).toHaveBeenCalledTimes(2) }) diff --git a/src/__tests__/extensions/sessionrecording.js b/src/__tests__/extensions/sessionrecording.js index 5e82f511e3..4e8c5fd942 100644 --- a/src/__tests__/extensions/sessionrecording.js +++ b/src/__tests__/extensions/sessionrecording.js @@ -71,6 +71,15 @@ describe('SessionRecording', () => { expect(given.sessionRecording.submitRecordings).not.toHaveBeenCalled() expect(given.posthog.persistence.register).toHaveBeenCalledWith({ [SESSION_RECORDING_ENABLED]: false }) }) + + it('does not start session recording if enabled via server but not client', () => { + given('response', () => ({ sessionRecording: { endpoint: '/ses/' } })) + given('disabled', () => true) + given.subject() + + expect(given.sessionRecording.submitRecordings).not.toHaveBeenCalled() + expect(given.posthog.persistence.register).toHaveBeenCalledWith({ [SESSION_RECORDING_ENABLED]: false }) + }) }) describe('recording', () => { diff --git a/src/autocapture.js b/src/autocapture.js index d1e710580b..7a1d9f8d9a 100644 --- a/src/autocapture.js +++ b/src/autocapture.js @@ -244,7 +244,12 @@ var autocapture = { this._initializedTokens.push(token) - if (response && response['config'] && response['config']['enable_collect_everything'] === true) { + if ( + response && + response['config'] && + response['config']['enable_collect_everything'] === true && + instance.get_config('autocapture') + ) { if (response['custom_properties']) { this._customProperties = response['custom_properties'] } diff --git a/src/extensions/sessionrecording.js b/src/extensions/sessionrecording.js index 95e2a12037..7ddec31213 100644 --- a/src/extensions/sessionrecording.js +++ b/src/extensions/sessionrecording.js @@ -16,17 +16,22 @@ export class SessionRecording { } startRecordingIfEnabled() { - if (this.instance.get_property(SESSION_RECORDING_ENABLED)) { + if ( + this.instance.get_property(SESSION_RECORDING_ENABLED) && + !this.instance.get_config('disable_session_recording') + ) { this._startCapture() } } afterDecideResponse(response) { + const enableRecordings = + !this.instance.get_config('disable_session_recording') && !!response['sessionRecording'] if (this.instance.persistence) { - this.instance.persistence.register({ [SESSION_RECORDING_ENABLED]: !!response['sessionRecording'] }) + this.instance.persistence.register({ [SESSION_RECORDING_ENABLED]: enableRecordings }) } - if (response['sessionRecording']) { + if (enableRecordings) { if (response['sessionRecording'].endpoint) { this.endpoint = response['sessionRecording'].endpoint } diff --git a/src/posthog-core.js b/src/posthog-core.js index aaff133217..845fd80920 100644 --- a/src/posthog-core.js +++ b/src/posthog-core.js @@ -146,12 +146,6 @@ var create_mplib = function (token, config, name) { instance.sessionRecording = new SessionRecording(instance) instance.sessionRecording.startRecordingIfEnabled() - if (!instance.get_config('advanced_disable_decide')) { - // As a reminder, if the /decide endpoint is disabled, feature flags, toolbar, session recording, autocapture, - // and compression will not be available. - new Decide(instance).call() - } - instance['__autocapture_enabled'] = instance.get_config('autocapture') if (instance.get_config('autocapture')) { var num_buckets = 100 @@ -167,6 +161,12 @@ var create_mplib = function (token, config, name) { } } + if (!instance.get_config('advanced_disable_decide')) { + // As a reminder, if the /decide endpoint is disabled, feature flags, toolbar, session recording, autocapture, + // and compression will not be available. + new Decide(instance).call() + } + // if any instance on the page has debug = true, we set the // global debug to be true Config.DEBUG = Config.DEBUG || instance.get_config('debug')