Skip to content

Commit

Permalink
Merge branch 'main' into chore/pd/browserslist
Browse files Browse the repository at this point in the history
  • Loading branch information
pauldambra authored Dec 24, 2024
2 parents f676240 + 5a5b4c4 commit d045e91
Show file tree
Hide file tree
Showing 56 changed files with 1,060 additions and 449 deletions.
46 changes: 46 additions & 0 deletions .github/workflows/minimum-version-ts-check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Library checks

on:
pull_request:
push:
branches:
- main

jobs:
unit:
name: Check minimum TS version
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 16, 18, 20, 22, 23 ]
typescript-version: ['4.7.2', '5.5.4', 'latest' ]
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 8.x.x

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'

- name: Install dependencies
run: pnpm install

- name: Build project
run: pnpm run build

# once its built we can see if we can use it on the forced version
- name: Override TypeScript version
run: pnpm add typescript@${{ matrix.typescript-version }} --save-dev

- name: Test TypeScript Import
run: |
rm test.ts || true
echo "import { posthog } from './dist/module'; console.log(posthog);" > test.ts
pnpm exec tsc test.ts --strict --target es2017 --module commonjs --moduleResolution node --noEmit --skipLibCheck
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,48 @@
## 1.203.1 - 2024-12-20

- fix: recordings that are paused for their whole duration (#1626)
- feat: custom event with remote config (#1628)

## 1.203.0 - 2024-12-20

- chore: add feature name prop to event (#1624)
- fix: payload host deny list (#1627)
- chore: build against lowest and current version of TS (#1625)
- chore: fix changelog after automation error (#1623)

## 1.202.5 - 2024-12-19

- chore: lint a file that needs it (#1622)

## 1.202.4 - 2024-12-19

- fix: do not even send heatmap with no x or y (#1621)
- fix: type error accessing null object and added test case (#1620)
- chore: Small tweaks to make fetch default (#1610)
- chore: remove circular dependencies (#1618)

## 1.202.3 - 2024-12-18

> NB an error meant this version was never published to NPM
## 1.202.2 - 2024-12-17

- fix: specify transport in fewer places to let config control (#1614)
- chore: update rollup, bc y not (#1615)

## 1.202.1 - 2024-12-17

- feat: Use token scoped remote config (#1611)

## 1.202.0 - 2024-12-17

- fix: Change default transport to fetch (#1612)

## 1.201.1 - 2024-12-17

- fix: Load RemoteConfig by default but don't use it (#1607)
- chore: add websockets example to nextjs playground (#1605)

## 1.201.0 - 2024-12-16

- fix: rotate session id proactively (#1512)
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ we use pnpm.
it's best to install using `npm install -g pnpm@latest-8`
and then `pnpm` commands as usual

##

## Testing

Unit tests: run `pnpm test`.
Expand Down
97 changes: 53 additions & 44 deletions cypress/e2e/session-recording.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,28 @@ function expectPageViewCustomEvent(snapshot: RRWebCustomEvent) {
expect(snapshot.data.tag).to.equal('$pageview')
}

function expectSessionIdChangedCustomEvent(snapshot: RRWebCustomEvent) {
function expectCustomEvent(snapshot: RRWebCustomEvent, tag: string) {
expect(snapshot.type).to.equal(5)
expect(snapshot.data.tag).to.equal('$session_id_change')
expect(snapshot.data.payload.changeReason).to.deep.equal({
noSessionId: true,
activityTimeout: true,
sessionPastMaximumLength: false,
})
expect(snapshot.data.tag).to.equal(tag)
}

function expectRemoteConfigCustomEvent(snapshot: RRWebCustomEvent) {
expectCustomEvent(snapshot, '$remote_config_received')
}

function expectPostHogConfigCustomEvent(snapshot: RRWebCustomEvent) {
expect(snapshot.type).to.equal(5)
expect(snapshot.data.tag).to.equal('$posthog_config')
expectCustomEvent(snapshot, '$posthog_config')
}

function expectSessionOptionsCustomEvent(snapshot: RRWebCustomEvent) {
expect(snapshot.type).to.equal(5)
expect(snapshot.data.tag).to.equal('$session_options')
expectCustomEvent(snapshot, '$session_options')
}

function sortByTag(snapshots: RRWebCustomEvent[]) {
return snapshots.sort((a, b) => a.data.tag?.localeCompare(b.data.tag))
}

function ensureActivitySendsSnapshots(initial = true) {
function ensureActivitySendsSnapshots(expectedCustomTags: string[] = []) {
cy.resetPhCaptures()

cy.get('[data-cy-input]')
Expand All @@ -63,23 +60,30 @@ function ensureActivitySendsSnapshots(initial = true) {
const capturedSnapshot = captures.find((e) => e.event === '$snapshot')
expect(capturedSnapshot).not.to.be.undefined

expect(capturedSnapshot['properties']['$snapshot_data']).to.have.length.above(14).and.below(40)
// a meta and then a full snapshot
expect(capturedSnapshot['properties']['$snapshot_data'][0].type).to.equal(4) // meta
expect(capturedSnapshot['properties']['$snapshot_data'][1].type).to.equal(2) // full_snapshot

if (initial) {
expectSessionOptionsCustomEvent(capturedSnapshot['properties']['$snapshot_data'][2])
expectPostHogConfigCustomEvent(capturedSnapshot['properties']['$snapshot_data'][3])
} else {
expectSessionOptionsCustomEvent(capturedSnapshot['properties']['$snapshot_data'][2])
expectPostHogConfigCustomEvent(capturedSnapshot['properties']['$snapshot_data'][3])
expectSessionIdChangedCustomEvent(capturedSnapshot['properties']['$snapshot_data'][4])
const capturedSnapshotData = capturedSnapshot['properties']['$snapshot_data']
expect(capturedSnapshotData).to.have.length.above(14).and.below(40)

// first a meta and then a full snapshot
expect(capturedSnapshotData.shift().type).to.equal(4)
expect(capturedSnapshotData.shift().type).to.equal(2)

// now the list should be all custom events until it is incremental
// and then only incremental snapshots
const customEvents = []
let seenIncremental = false
for (const snapshot of capturedSnapshotData) {
if (snapshot.type === 5) {
expect(seenIncremental).to.be.false
customEvents.push(snapshot)
} else if (snapshot.type === 3) {
seenIncremental = true
} else {
throw new Error(`Unexpected snapshot type: ${snapshot.type}`)
}
}

// Making a set from the rest should all be 3 - incremental snapshots
const remainder = capturedSnapshot['properties']['$snapshot_data'].slice(initial ? 4 : 5)
expect(Array.from(new Set(remainder.map((s) => s.type)))).to.deep.equal([3])
const customEventTags = customEvents.map((s) => s.data.tag)
cy.log('checked custom event tags', { customEventTags, expectedCustomTags })
expect(customEventTags).to.eql(expectedCustomTags)
})
})
}
Expand Down Expand Up @@ -145,10 +149,11 @@ describe('Session recording', () => {
// a meta and then a full snapshot
expect(captures[1]['properties']['$snapshot_data'][0].type).to.equal(4) // meta
expect(captures[1]['properties']['$snapshot_data'][1].type).to.equal(2) // full_snapshot
expect(captures[1]['properties']['$snapshot_data'][2].type).to.equal(5) // custom event with options
expect(captures[1]['properties']['$snapshot_data'][3].type).to.equal(5) // custom event with posthog config
expect(captures[1]['properties']['$snapshot_data'][2].type).to.equal(5) // custom event with remote config
expect(captures[1]['properties']['$snapshot_data'][3].type).to.equal(5) // custom event with options
expect(captures[1]['properties']['$snapshot_data'][4].type).to.equal(5) // custom event with posthog config
// Making a set from the rest should all be 3 - incremental snapshots
const incrementalSnapshots = captures[1]['properties']['$snapshot_data'].slice(4)
const incrementalSnapshots = captures[1]['properties']['$snapshot_data'].slice(5)
expect(Array.from(new Set(incrementalSnapshots.map((s) => s.type)))).to.deep.eq([3])

expect(captures[2]['properties']['$session_recording_start_reason']).to.equal(
Expand Down Expand Up @@ -249,13 +254,13 @@ describe('Session recording', () => {
[/http:\/\/localhost:\d+\/static\/array.js/, 'script'],
[
/http:\/\/localhost:\d+\/decide\/\?v=3&ip=1&_=\d+&ver=1\.\d\d\d\.\d+&compression=base64/,
'xmlhttprequest',
'fetch',
],
[/http:\/\/localhost:\d+\/static\/recorder.js\?v=1\.\d\d\d\.\d+/, 'script'],
[/https:\/\/example.com/, 'fetch'],
]

// yay, includes expected type 6 network data
// yay, includes expected network data
expect(capturedRequests.length).to.equal(expectedCaptureds.length)
expectedCaptureds.forEach(([url, initiatorType], index) => {
expect(capturedRequests[index].name).to.match(url)
Expand All @@ -281,7 +286,7 @@ describe('Session recording', () => {
})
})

it('it captures XHR method correctly', () => {
it('it captures XHR/fetch methods correctly', () => {
cy.get('[data-cy-xhr-call-button]').click()
cy.wait('@example.com')
cy.wait('@session-recording')
Expand All @@ -304,17 +309,19 @@ describe('Session recording', () => {
[/http:\/\/localhost:\d+\/static\/array.js/, 'script'],
[
/http:\/\/localhost:\d+\/decide\/\?v=3&ip=1&_=\d+&ver=1\.\d\d\d\.\d+&compression=base64/,
'xmlhttprequest',
'fetch',
],
[/http:\/\/localhost:\d+\/static\/recorder.js\?v=1\.\d\d\d\.\d+/, 'script'],
[/https:\/\/example.com/, 'xmlhttprequest'],
]

// yay, includes expected type 6 network data
// yay, includes expected network data
expect(capturedRequests.length).to.equal(expectedCaptureds.length)
expectedCaptureds.forEach(([url, initiatorType], index) => {
expect(capturedRequests[index].name).to.match(url)
expect(capturedRequests[index].initiatorType).to.equal(initiatorType)
const capturedRequest = capturedRequests[index]

expect(capturedRequest.name).to.match(url)
expect(capturedRequest.initiatorType).to.equal(initiatorType)
})

// the HTML file that cypress is operating on (playground/cypress/index.html)
Expand Down Expand Up @@ -372,7 +379,7 @@ describe('Session recording', () => {

cy.get('[data-cy-input]').type('hello world! ')
cy.wait(500)
ensureActivitySendsSnapshots()
ensureActivitySendsSnapshots(['$remote_config_received', '$session_options', '$posthog_config'])
cy.posthog().then((ph) => {
ph.stopSessionRecording()
})
Expand All @@ -383,7 +390,7 @@ describe('Session recording', () => {
cy.posthog().then((ph) => {
ph.startSessionRecording()
})
ensureActivitySendsSnapshots()
ensureActivitySendsSnapshots(['$session_options', '$posthog_config'])

// the session id is not rotated by stopping and starting the recording
cy.posthog().then((ph) => {
Expand Down Expand Up @@ -522,14 +529,16 @@ describe('Session recording', () => {
capturedSnapshot['properties']['$snapshot_data'][2],
capturedSnapshot['properties']['$snapshot_data'][3],
capturedSnapshot['properties']['$snapshot_data'][4],
capturedSnapshot['properties']['$snapshot_data'][5],
])

expectPageViewCustomEvent(customEvents[0])
expectPostHogConfigCustomEvent(customEvents[1])
expectSessionOptionsCustomEvent(customEvents[2])
expectRemoteConfigCustomEvent(customEvents[2])
expectSessionOptionsCustomEvent(customEvents[3])

const xPositions = []
for (let i = 5; i < capturedSnapshot['properties']['$snapshot_data'].length; i++) {
for (let i = 6; i < capturedSnapshot['properties']['$snapshot_data'].length; i++) {
expect(capturedSnapshot['properties']['$snapshot_data'][i].type).to.equal(3)
expect(capturedSnapshot['properties']['$snapshot_data'][i].data.source).to.equal(
6,
Expand Down Expand Up @@ -626,14 +635,14 @@ describe('Session recording', () => {
startingSessionId = ph.get_session_id()
})

ensureActivitySendsSnapshots()
ensureActivitySendsSnapshots(['$remote_config_received', '$session_options', '$posthog_config'])

cy.posthog().then((ph) => {
cy.log('resetting posthog')
ph.reset()
})

ensureActivitySendsSnapshots(false)
ensureActivitySendsSnapshots(['$session_options', '$posthog_config', '$session_id_change'])

// the session id is rotated after reset is called
cy.posthog().then((ph) => {
Expand Down
8 changes: 8 additions & 0 deletions cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ Cypress.on('window:before:load', (win) => {
cy.spy(win.console, 'warn')
cy.spy(win.console, 'log')
cy.spy(win.console, 'debug')

// NOTE: Temporary change whilst testing remote config
;(win as any)._POSTHOG_REMOTE_CONFIG = {
test_token: {
config: {},
siteApps: [],
},
}
})

beforeEach(() => {
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "posthog-js",
"version": "1.201.0",
"version": "1.203.1",
"description": "Posthog-js allows you to automatically capture usage and send events to PostHog.",
"repository": "https://github.com/PostHog/posthog-js",
"author": "[email protected]",
Expand Down Expand Up @@ -56,9 +56,9 @@
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.1",
"@rollup/plugin-typescript": "^12.1.2",
"@rrweb/record": "2.0.0-alpha.17",
"@rrweb/rrweb-plugin-console-record": "2.0.0-alpha.17",
"@rrweb/types": "2.0.0-alpha.17",
Expand Down Expand Up @@ -87,7 +87,7 @@
"eslint-plugin-jest": "^28.8.3",
"eslint-plugin-no-only-tests": "^3.1.0",
"eslint-plugin-posthog-js": "link:eslint-rules",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.30.1",
"eslint-plugin-react-hooks": "^4.6.0",
"expect": "^29.7.0",
Expand All @@ -102,8 +102,8 @@
"node-fetch": "^2.6.11",
"posthog-js": "link:",
"preact-render-to-string": "^6.3.1",
"prettier": "^2.7.1",
"rollup": "^4.24.0",
"prettier": "^3.4.2",
"rollup": "^4.28.1",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-visualizer": "^5.12.0",
"sinon": "9.0.2",
Expand Down
2 changes: 2 additions & 0 deletions playground/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"posthog-js": "1.200.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"socket.io": "^4.8.1",
"socket.io-client": "^4.8.1",
"typescript": "4.9.5"
},
"devDependencies": {
Expand Down
7 changes: 7 additions & 0 deletions playground/nextjs/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ export default function App({ Component, pageProps }: AppProps) {
}
}, [])

useEffect(() => {
// make sure we initialize the WebSocket server
// we don't need to support IE11 here
// eslint-disable-next-line compat/compat
fetch('/api/socket')
}, [])

const localhostDomain = process.env.NEXT_PUBLIC_CROSSDOMAIN
? 'https://localhost:8000'
: process.env.NEXT_PUBLIC_POSTHOG_HOST
Expand Down
Loading

0 comments on commit d045e91

Please sign in to comment.