diff --git a/src/app/providers/providers.component.ts b/src/app/providers/providers.component.ts index 59d5dfa..20b13fb 100644 --- a/src/app/providers/providers.component.ts +++ b/src/app/providers/providers.component.ts @@ -94,6 +94,11 @@ export class ProvidersComponent { display: 'Matomo', type: 'Analytics', }, + { + name: 'posthog', + display: 'Posthog', + type: 'Analytics', + }, { name: 'segment', display: 'Segment', diff --git a/src/assets/svg/posthog.svg b/src/assets/svg/posthog.svg new file mode 100644 index 0000000..0a64d1d --- /dev/null +++ b/src/assets/svg/posthog.svg @@ -0,0 +1 @@ + diff --git a/src/lib/providers/posthog/README.md b/src/lib/providers/posthog/README.md new file mode 100644 index 0000000..e01931c --- /dev/null +++ b/src/lib/providers/posthog/README.md @@ -0,0 +1,11 @@ + + +# Posthog + +**homepage**: [posthog.com](https://posthog.com/) +**docs**: [posthog.com/docs/product-analytics](https://posthog.com/docs/product-analytics) +**import**: `import { Angulartics2Posthog } from 'angulartics2';` diff --git a/src/lib/providers/posthog/posthog.spec.ts b/src/lib/providers/posthog/posthog.spec.ts new file mode 100644 index 0000000..f0f1dc3 --- /dev/null +++ b/src/lib/providers/posthog/posthog.spec.ts @@ -0,0 +1,169 @@ +import { fakeAsync, inject, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { advance, createRoot, RootCmp, TestModule } from '../../test.mocks'; + +import { Angulartics2 } from '../../angulartics2-core'; +import { Angulartics2Posthog } from './posthog'; + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; +declare var window: any; + +describe('Angulartics2Posthog', () => { + let fixture: ComponentFixture; + let posthog: any; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [TestModule], + providers: [Angulartics2Posthog], + }); + + window.posthog = posthog = { + capture: jasmine.createSpy('capture'), + identify: jasmine.createSpy('identify'), + alias: jasmine.createSpy('alias'), + }; + + const provider: Angulartics2Posthog = TestBed.inject(Angulartics2Posthog); + provider.startTracking(); + }); + + it('should track pages', fakeAsync( + inject( + [Angulartics2, Angulartics2Posthog], + (angulartics2: Angulartics2, angulartics2Posthog: Angulartics2Posthog) => { + fixture = createRoot(RootCmp); + angulartics2.pageTrack.next({ path: '/abc' }); + advance(fixture); + expect(posthog.capture).toHaveBeenCalledWith('Page Viewed', { + page: '/abc', + }); + }, + ), + )); + + it('should track events', fakeAsync( + inject( + [Angulartics2, Angulartics2Posthog], + (angulartics2: Angulartics2, angulartics2Posthog: Angulartics2Posthog) => { + fixture = createRoot(RootCmp); + angulartics2.eventTrack.next({ + action: 'do', + properties: { category: 'cat' }, + }); + advance(fixture); + expect(posthog.capture).toHaveBeenCalledWith('do', { category: 'cat' }); + }, + ), + )); + + it('should set username', fakeAsync( + inject( + [Angulartics2, Angulartics2Posthog], + (angulartics2: Angulartics2, angulartics2Posthog: Angulartics2Posthog) => { + fixture = createRoot(RootCmp); + angulartics2.setUsername.next('testUser'); + advance(fixture); + expect(posthog.identify).toHaveBeenCalledWith('testUser'); + }, + ), + )); + + it('should set user properties', fakeAsync( + inject( + [Angulartics2, Angulartics2Posthog], + (angulartics2: Angulartics2, angulartics2Posthog: Angulartics2Posthog) => { + fixture = createRoot(RootCmp); + angulartics2.setUserProperties.next({ + distinct_id: '1', + firstName: 'John', + lastName: 'Doe', + }); + advance(fixture); + expect(posthog.identify).toHaveBeenCalledWith('1', { + distinct_id: '1', + firstName: 'John', + lastName: 'Doe', + }); + }, + ), + )); + + it('should set user properties once', fakeAsync( + inject( + [Angulartics2, Angulartics2Posthog], + (angulartics2: Angulartics2, angulartics2Posthog: Angulartics2Posthog) => { + fixture = createRoot(RootCmp); + angulartics2.setUserPropertiesOnce.next({ + distinct_id: '1', + firstName: 'John', + lastName: 'Doe', + }); + advance(fixture); + expect(posthog.capture).toHaveBeenCalledWith('Set User Properties Once', { + $set_once: { + distinct_id: '1', + firstName: 'John', + lastName: 'Doe', + } + }); + }, + ), + )); + + it('should set super properties', fakeAsync( + inject( + [Angulartics2, Angulartics2Posthog], + (angulartics2: Angulartics2, angulartics2Posthog: Angulartics2Posthog) => { + fixture = createRoot(RootCmp); + angulartics2.setSuperProperties.next({ + distinct_id: '1', + firstName: 'John', + lastName: 'Doe', + }); + advance(fixture); + expect(posthog.capture).toHaveBeenCalledWith('Set Super Properties', { + $set: { + distinct_id: '1', + firstName: 'John', + lastName: 'Doe', + } + }); + }, + ), + )); + + it('should set super properties once', fakeAsync( + inject( + [Angulartics2, Angulartics2Posthog], + (angulartics2: Angulartics2, angulartics2Posthog: Angulartics2Posthog) => { + fixture = createRoot(RootCmp); + angulartics2.setSuperPropertiesOnce.next({ + distinct_id: '1', + firstName: 'John', + lastName: 'Doe', + }); + advance(fixture); + expect(posthog.capture).toHaveBeenCalledWith('Set Super Properties Once', { + $set_once: { + distinct_id: '1', + firstName: 'John', + lastName: 'Doe', + } + }); + }, + ), + )); + + it('should set alias', fakeAsync( + inject( + [Angulartics2, Angulartics2Posthog], + (angulartics2: Angulartics2, angulartics2Posthog: Angulartics2Posthog) => { + fixture = createRoot(RootCmp); + angulartics2.setAlias.next('testAlias'); + advance(fixture); + expect(posthog.alias).toHaveBeenCalledWith('testAlias'); + }, + ), + )); +}); diff --git a/src/lib/providers/posthog/posthog.ts b/src/lib/providers/posthog/posthog.ts new file mode 100644 index 0000000..2e68df6 --- /dev/null +++ b/src/lib/providers/posthog/posthog.ts @@ -0,0 +1,119 @@ +import { inject, Injectable } from '@angular/core'; +import { Angulartics2 } from '../../angulartics2-core'; + +declare let posthog: any; + +@Injectable({ providedIn: 'root' }) +export class Angulartics2Posthog { + private readonly angulartics2 = inject(Angulartics2); + + constructor() { + this.angulartics2.setUsername.subscribe(x => this.setUsername(x)); + this.angulartics2.setUserProperties.subscribe(x => this.setUserProperties(x)); + this.angulartics2.setUserPropertiesOnce.subscribe(x => this.setUserPropertiesOnce(x)); + this.angulartics2.setSuperProperties.subscribe(x => this.setSuperProperties(x)); + this.angulartics2.setSuperPropertiesOnce.subscribe(x => this.setSuperPropertiesOnce(x)); + this.angulartics2.setAlias.subscribe(x => this.setAlias(x)); + } + + startTracking(): void { + this.startPageTracking(); + this.startEventTracking(); + } + + startPageTracking(): void { + this.angulartics2.pageTrack.pipe(this.angulartics2.filterDeveloperMode()).subscribe(x => { + this.pageTrack(x.path); + }); + } + + startEventTracking(): void { + this.angulartics2.eventTrack.pipe(this.angulartics2.filterDeveloperMode()).subscribe(x => { + this.eventTrack(x.action, x.properties); + }); + } + + pageTrack(path: string | undefined): void { + try { + posthog.capture('Page Viewed', { page: path }); + } catch (e) { + if (!(e instanceof ReferenceError)) { + throw e; + } + } + } + + eventTrack(action: string, properties?: any): void { + try { + posthog.capture(action, properties); + } catch (e) { + if (!(e instanceof ReferenceError)) { + throw e; + } + } + } + + setUsername(userId: string | { userId: string | number }): void { + try { + posthog.identify(userId); + } catch (e) { + if (!(e instanceof ReferenceError)) { + throw e; + } + } + } + + setUserProperties(properties: any | undefined): void { + try { + posthog.identify(properties["distinct_id"], properties); + } catch (e) { + if (!(e instanceof ReferenceError)) { + throw e; + } + } + } + + setUserPropertiesOnce(properties: any | undefined): void { + try { + posthog.capture('Set User Properties Once', { + $set_once: properties + }); + } + catch (e) { + if (!(e instanceof ReferenceError)) { + throw e; + } + } + } + + setSuperProperties(properties: any | undefined): void { + try { + posthog.capture('Set Super Properties', { $set: properties }); + } catch (e) { + if (!(e instanceof ReferenceError)) { + throw e; + } + } + } + + setSuperPropertiesOnce(properties: any | undefined): void { + try { + posthog.capture('Set Super Properties Once', { $set_once: properties }); + } catch (e) { + if (!(e instanceof ReferenceError)) { + throw e; + } + } + } + + setAlias(alias: string): void { + try { + posthog.alias(alias); + } + catch (e) { + if (!(e instanceof ReferenceError)) { + throw e; + } + } + } +} diff --git a/src/lib/public_api.ts b/src/lib/public_api.ts index 12c9321..4a9711f 100644 --- a/src/lib/public_api.ts +++ b/src/lib/public_api.ts @@ -20,6 +20,7 @@ export * from './providers/hubspot/hubspot'; export * from './providers/kissmetrics/kissmetrics'; export * from './providers/launch/launch'; export * from './providers/mixpanel/mixpanel'; +export * from './providers/posthog/posthog'; export * from './providers/pyze/pyze'; export * from './providers/matomo/matomo'; export * from './providers/segment/segment';