From daa0a27ab741c1c902e0c5e74d9bdd55701a4f9a Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Tue, 17 Dec 2024 09:35:33 +0100 Subject: [PATCH] feat(cdp): test site function javascript (#26943) Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- posthog/cdp/site_functions.py | 4 +- posthog/cdp/test/test_site_functions.py | 73 +++++++++++++++++-- .../test/__snapshots__/test_in_cohort.ambr | 4 +- posthog/models/test/test_remote_config.py | 8 +- 4 files changed, 75 insertions(+), 14 deletions(-) diff --git a/posthog/cdp/site_functions.py b/posthog/cdp/site_functions.py index 690dc136ea577..f6ece18e28792 100644 --- a/posthog/cdp/site_functions.py +++ b/posthog/cdp/site_functions.py @@ -92,7 +92,7 @@ def get_transpiled_function(hog_function: HogFunction) -> str: """ let processEvent = undefined; if ('onEvent' in source) { - processEvent = function processEvent(globals) { + processEvent = function processEvent(globals, posthog) { if (!('onEvent' in source)) { return; }; const inputs = buildInputs(globals); const filterGlobals = { ...globals.groups, ...globals.event, person: globals.person, inputs, pdi: { distinct_id: globals.event.distinct_id, person: globals.person } }; @@ -123,7 +123,7 @@ def get_transpiled_function(hog_function: HogFunction) -> str: } return { - processEvent: processEvent + processEvent: (globals) => processEvent(globals, posthog) } } diff --git a/posthog/cdp/test/test_site_functions.py b/posthog/cdp/test/test_site_functions.py index 9370cb7266740..0b6c0bc1bb8a6 100644 --- a/posthog/cdp/test/test_site_functions.py +++ b/posthog/cdp/test/test_site_functions.py @@ -1,3 +1,4 @@ +import json import subprocess import tempfile from inline_snapshot import snapshot @@ -71,7 +72,7 @@ def test_get_transpiled_function_basic(self): };return exports;})(); let processEvent = undefined; if ('onEvent' in source) { - processEvent = function processEvent(globals) { + processEvent = function processEvent(globals, posthog) { if (!('onEvent' in source)) { return; }; const inputs = buildInputs(globals); const filterGlobals = { ...globals.groups, ...globals.event, person: globals.person, inputs, pdi: { distinct_id: globals.event.distinct_id, person: globals.person } }; @@ -98,7 +99,7 @@ def test_get_transpiled_function_basic(self): } return { - processEvent: processEvent + processEvent: (globals) => processEvent(globals, posthog) } } @@ -129,12 +130,12 @@ def test_get_transpiled_function_with_template_input(self): assert '__getGlobal("person")' in result def test_get_transpiled_function_with_filters(self): - self.hog_function.hog = "export function onEvent(event) { console.log(event.event); }" + self.hog_function.hog = "export function onEvent(globals) { console.log(globals); }" self.hog_function.filters = {"events": [{"id": "$pageview", "name": "$pageview", "type": "events", "order": 0}]} result = self.compile_and_run() - assert "console.log(event.event);" in result + assert "console.log(globals);" in result assert "const filterMatches = " in result assert '__getGlobal("event") == "$pageview"' in result assert "const filterMatches = !!(!!((__getGlobal" in result @@ -249,7 +250,7 @@ def test_get_transpiled_function_with_complex_filters(self): action.steps = [{"event": "$pageview", "url": "https://example.com"}] # type: ignore action.save() - self.hog_function.hog = "export function onEvent(event) { console.log(event.event); }" + self.hog_function.hog = "export function onEvent(globals) { console.log(globals); }" self.hog_function.filters = { "events": [{"id": "$pageview", "name": "$pageview", "type": "events"}], "actions": [{"id": str(action.pk), "name": "Test Action", "type": "actions"}], @@ -258,7 +259,7 @@ def test_get_transpiled_function_with_complex_filters(self): result = self.compile_and_run() - assert "console.log(event.event);" in result + assert "console.log(globals);" in result assert "const filterMatches = " in result assert '__getGlobal("event") == "$pageview"' in result assert "https://example.com" in result @@ -283,3 +284,63 @@ def test_get_transpiled_function_with_mappings(self): assert 'if (!!(!!((__getGlobal("event") == "$autocapture")))) {' in result assert "const newInputs = structuredClone(inputs);" in result assert 'newInputs["greeting"] = concat("Hallo, ", __getProperty' in result + + def test_run_function_onload(self): + self.hog_function.hog = "export function onLoad({ inputs, posthog }) { console.log(inputs.message); }" + self.hog_function.filters = {"events": [{"id": "$pageview", "name": "$pageview", "type": "events", "order": 0}]} + self.hog_function.inputs = {"message": {"value": "Hello World {person.properties.name}"}} + + result = self.compile_and_run() + assert "Hello World" in result + + response = self._execute_javascript( + result + + "().init({ posthog: { get_property: () => ({name: 'Bob'}) }, callback: () => { console.log('Loaded') } })" + ) + assert "Hello World Bob\nLoaded" == response.strip() + + def test_run_function_onevent(self): + self.hog_function.hog = "export function onEvent({ inputs }) { console.log(inputs.message); }" + # self.hog_function.filters = {"events": [{"id": "$pageview", "name": "$pageview", "type": "events", "order": 0}]} + self.hog_function.inputs = {"message": {"value": "Hello World {event.properties.id}"}} + self.hog_function.mappings = [ + { + "inputs": {"greeting": {"value": "Hallo, {person.properties.nonexistent_property}!"}}, + "filters": {"events": [{"id": "$pageview", "name": "$pageview", "type": "events"}]}, + } + ] + + result = self.compile_and_run() + assert "Hello World" in result + + globals = { + "event": {"event": "$pageview", "properties": {"id": "banana"}}, + "groups": {}, + "person": {"properties": {"name": "Bob"}}, + } + response = self._execute_javascript( + result + + "().init({ posthog: { get_property: () => ({name: 'Bob'}) }, callback: () => { console.log('Loaded') } }).processEvent(" + + json.dumps(globals) + + ")" + ) + assert "Loaded\nHello World banana" == response.strip() + + globals = { + "event": {"event": "$autocapture", "properties": {"id": "banana"}}, + "groups": {}, + "person": {"properties": {"name": "Bob"}}, + } + response = self._execute_javascript( + result + + "().init({ posthog: { get_property: () => ({name: 'Bob'}) }, callback: () => { console.log('Loaded') } }).processEvent(" + + json.dumps(globals) + + ")" + ) + assert "Loaded" == response.strip() + + def _execute_javascript(self, js) -> str: + with tempfile.NamedTemporaryFile(delete=False) as f: + f.write(js.encode("utf-8")) + f.flush() + return subprocess.check_output(["node", f.name]).decode("utf-8") diff --git a/posthog/hogql/transforms/test/__snapshots__/test_in_cohort.ambr b/posthog/hogql/transforms/test/__snapshots__/test_in_cohort.ambr index ce7faf7612056..804183be31efe 100644 --- a/posthog/hogql/transforms/test/__snapshots__/test_in_cohort.ambr +++ b/posthog/hogql/transforms/test/__snapshots__/test_in_cohort.ambr @@ -42,7 +42,7 @@ FROM events LEFT JOIN ( SELECT person_id AS cohort_person_id, 1 AS matched, cohort_id FROM static_cohort_people - WHERE in(cohort_id, [8])) AS __in_cohort ON equals(__in_cohort.cohort_person_id, person_id) + WHERE in(cohort_id, [11])) AS __in_cohort ON equals(__in_cohort.cohort_person_id, person_id) WHERE and(1, equals(__in_cohort.matched, 1)) LIMIT 100 ''' @@ -66,7 +66,7 @@ FROM events LEFT JOIN ( SELECT person_id AS cohort_person_id, 1 AS matched, cohort_id FROM static_cohort_people - WHERE in(cohort_id, [9])) AS __in_cohort ON equals(__in_cohort.cohort_person_id, person_id) + WHERE in(cohort_id, [12])) AS __in_cohort ON equals(__in_cohort.cohort_person_id, person_id) WHERE and(1, equals(__in_cohort.matched, 1)) LIMIT 100 ''' diff --git a/posthog/models/test/test_remote_config.py b/posthog/models/test/test_remote_config.py index 7bb985b78de6c..d9565e2422ddb 100644 --- a/posthog/models/test/test_remote_config.py +++ b/posthog/models/test/test_remote_config.py @@ -700,7 +700,7 @@ def test_renders_js_including_site_functions(self): const source = (function () {let exports={};"use strict";;return exports;})(); let processEvent = undefined; if ('onEvent' in source) { - processEvent = function processEvent(globals) { + processEvent = function processEvent(globals, posthog) { if (!('onEvent' in source)) { return; }; const inputs = buildInputs(globals); const filterGlobals = { ...globals.groups, ...globals.event, person: globals.person, inputs, pdi: { distinct_id: globals.event.distinct_id, person: globals.person } }; @@ -727,7 +727,7 @@ def test_renders_js_including_site_functions(self): } return { - processEvent: processEvent + processEvent: (globals) => processEvent(globals, posthog) } } @@ -746,7 +746,7 @@ def test_renders_js_including_site_functions(self): const source = (function () {let exports={};"use strict";;return exports;})(); let processEvent = undefined; if ('onEvent' in source) { - processEvent = function processEvent(globals) { + processEvent = function processEvent(globals, posthog) { if (!('onEvent' in source)) { return; }; const inputs = buildInputs(globals); const filterGlobals = { ...globals.groups, ...globals.event, person: globals.person, inputs, pdi: { distinct_id: globals.event.distinct_id, person: globals.person } }; @@ -773,7 +773,7 @@ def test_renders_js_including_site_functions(self): } return { - processEvent: processEvent + processEvent: (globals) => processEvent(globals, posthog) } }