@posthog/react 1.2.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,25 +10,25 @@ describe('PostHogErrorBoundary component', () => {
10
10
  mockFunction(console, 'warn')
11
11
  mockFunction(posthog, 'captureException')
12
12
 
13
- given('render_with_error', () => (props) => render(<RenderWithError {...props} />))
14
- given('render_without_error', () => (props) => render(<RenderWithoutError {...props} />))
13
+ const renderWithError = (props: any) => render(<RenderWithError {...props} />)
14
+ const renderWithoutError = (props?: any) => render(<RenderWithoutError {...props} />)
15
15
 
16
16
  it('should call captureException with error message', () => {
17
- const { container } = given.render_with_error({ message: 'Test error', fallback: <div></div> })
17
+ const { container } = renderWithError({ message: 'Test error', fallback: <div></div> })
18
18
  expect(posthog.captureException).toHaveBeenCalledWith(new Error('Test error'), undefined)
19
19
  expect(container.innerHTML).toBe('<div></div>')
20
20
  expect(console.error).toHaveBeenCalledTimes(2)
21
21
  })
22
22
 
23
23
  it('should warn user when fallback is null', () => {
24
- const { container } = given.render_with_error({ fallback: null })
24
+ const { container } = renderWithError({ fallback: null })
25
25
  expect(posthog.captureException).toHaveBeenCalledWith(new Error('Error'), undefined)
26
26
  expect(container.innerHTML).toBe('')
27
27
  expect(console.warn).toHaveBeenCalledWith(__POSTHOG_ERROR_MESSAGES.INVALID_FALLBACK)
28
28
  })
29
29
 
30
30
  it('should warn user when fallback is a string', () => {
31
- const { container } = given.render_with_error({ fallback: 'hello' })
31
+ const { container } = renderWithError({ fallback: 'hello' })
32
32
  expect(posthog.captureException).toHaveBeenCalledWith(new Error('Error'), undefined)
33
33
  expect(container.innerHTML).toBe('')
34
34
  expect(console.warn).toHaveBeenCalledWith(__POSTHOG_ERROR_MESSAGES.INVALID_FALLBACK)
@@ -36,15 +36,15 @@ describe('PostHogErrorBoundary component', () => {
36
36
 
37
37
  it('should add additional properties before sending event (as object)', () => {
38
38
  const props = { team_id: '1234' }
39
- given.render_with_error({ message: 'Kaboom', additionalProperties: props })
39
+ renderWithError({ message: 'Kaboom', additionalProperties: props })
40
40
  expect(posthog.captureException).toHaveBeenCalledWith(new Error('Kaboom'), props)
41
41
  })
42
42
 
43
43
  it('should add additional properties before sending event (as function)', () => {
44
44
  const props = { team_id: '1234' }
45
- given.render_with_error({
45
+ renderWithError({
46
46
  message: 'Kaboom',
47
- additionalProperties: (err) => {
47
+ additionalProperties: (err: Error) => {
48
48
  expect(err.message).toBe('Kaboom')
49
49
  return props
50
50
  },
@@ -53,7 +53,7 @@ describe('PostHogErrorBoundary component', () => {
53
53
  })
54
54
 
55
55
  it('should render children without errors', () => {
56
- const { container } = given.render_without_error()
56
+ const { container } = renderWithoutError()
57
57
  expect(container.innerHTML).toBe('<div>Amazing content</div>')
58
58
  })
59
59
  })
@@ -63,11 +63,11 @@ describe('captureException processing', () => {
63
63
  mockFunction(console, 'warn')
64
64
  mockFunction(posthog, 'capture')
65
65
 
66
- given('render_with_error', () => (props) => render(<RenderWithError {...props} />))
66
+ const renderWithError = (props: any) => render(<RenderWithError {...props} />)
67
67
 
68
68
  it('should call capture with a stacktrace', () => {
69
- given.render_with_error({ message: 'Kaboom', fallback: <div></div>, additionalProperties: {} })
70
- const captureCalls = posthog.capture.mock.calls
69
+ renderWithError({ message: 'Kaboom', fallback: <div></div>, additionalProperties: {} })
70
+ const captureCalls = (posthog.capture as jest.Mock).mock.calls
71
71
  expect(captureCalls.length).toBe(1)
72
72
  const exceptionList = captureCalls[0][1].$exception_list
73
73
  expect(exceptionList.length).toBe(1)
@@ -76,7 +76,7 @@ describe('captureException processing', () => {
76
76
  })
77
77
  })
78
78
 
79
- function mockFunction(object, funcName) {
79
+ function mockFunction(object: any, funcName: string) {
80
80
  const originalFunc = object[funcName]
81
81
 
82
82
  beforeEach(() => {
@@ -88,11 +88,11 @@ function mockFunction(object, funcName) {
88
88
  })
89
89
  }
90
90
 
91
- function ComponentWithError({ message }) {
91
+ function ComponentWithError({ message }: { message: string }): React.ReactElement {
92
92
  throw new Error(message)
93
93
  }
94
94
 
95
- function RenderWithError({ message = 'Error', fallback, additionalProperties }) {
95
+ function RenderWithError({ message = 'Error', fallback, additionalProperties }: any) {
96
96
  return (
97
97
  <PostHogErrorBoundary fallback={fallback} additionalProperties={additionalProperties}>
98
98
  <ComponentWithError message={message} />
@@ -100,7 +100,7 @@ function RenderWithError({ message = 'Error', fallback, additionalProperties })
100
100
  )
101
101
  }
102
102
 
103
- function RenderWithoutError({ additionalProperties }) {
103
+ function RenderWithoutError({ additionalProperties }: any) {
104
104
  return (
105
105
  <PostHogErrorBoundary fallback={<div></div>} additionalProperties={additionalProperties}>
106
106
  <div>Amazing content</div>
@@ -0,0 +1,249 @@
1
+ import * as React from 'react'
2
+ import { useState } from 'react'
3
+ import { render, screen, fireEvent } from '@testing-library/react'
4
+ import { PostHogProvider, PostHog } from '../../context'
5
+ import { PostHogFeature } from '../'
6
+ import '@testing-library/jest-dom'
7
+
8
+ const FEATURE_FLAG_STATUS: Record<string, string | boolean> = {
9
+ multivariate_feature: 'string-value',
10
+ example_feature_payload: 'test',
11
+ test: true,
12
+ test_false: false,
13
+ }
14
+
15
+ const FEATURE_FLAG_PAYLOADS: Record<string, any> = {
16
+ example_feature_payload: {
17
+ id: 1,
18
+ name: 'example_feature_1_payload',
19
+ key: 'example_feature_1_payload',
20
+ },
21
+ }
22
+
23
+ describe('PostHogFeature component', () => {
24
+ let posthog: PostHog
25
+
26
+ const renderWith = (instance: PostHog, flag = 'test', matchValue: string | boolean | undefined = true) =>
27
+ render(
28
+ <PostHogProvider client={instance}>
29
+ <PostHogFeature flag={flag} match={matchValue}>
30
+ <div data-testid="helloDiv">Hello</div>
31
+ </PostHogFeature>
32
+ </PostHogProvider>
33
+ )
34
+
35
+ beforeEach(() => {
36
+ // IntersectionObserver isn't available in test environment
37
+ const mockIntersectionObserver = jest.fn()
38
+ mockIntersectionObserver.mockReturnValue({
39
+ observe: () => null,
40
+ unobserve: () => null,
41
+ disconnect: () => null,
42
+ })
43
+
44
+ // eslint-disable-next-line compat/compat
45
+ window.IntersectionObserver = mockIntersectionObserver
46
+
47
+ posthog = {
48
+ isFeatureEnabled: (flag: string) => !!FEATURE_FLAG_STATUS[flag],
49
+ getFeatureFlag: (flag: string) => FEATURE_FLAG_STATUS[flag],
50
+ getFeatureFlagPayload: (flag: string) => FEATURE_FLAG_PAYLOADS[flag],
51
+ onFeatureFlags: (callback: any) => {
52
+ const activeFlags: string[] = []
53
+ for (const flag in FEATURE_FLAG_STATUS) {
54
+ if (FEATURE_FLAG_STATUS[flag]) {
55
+ activeFlags.push(flag)
56
+ }
57
+ }
58
+ callback(activeFlags)
59
+ return () => {}
60
+ },
61
+ capture: jest.fn(),
62
+ } as unknown as PostHog
63
+ })
64
+
65
+ it('should track interactions with the feature component', () => {
66
+ renderWith(posthog)
67
+
68
+ fireEvent.click(screen.getByTestId('helloDiv'))
69
+ expect(posthog.capture).toHaveBeenCalledWith('$feature_interaction', {
70
+ feature_flag: 'test',
71
+ $set: { '$feature_interaction/test': true },
72
+ })
73
+ expect(posthog.capture).toHaveBeenCalledTimes(1)
74
+ })
75
+
76
+ it('should not fire for every interaction with the feature component', () => {
77
+ renderWith(posthog)
78
+
79
+ fireEvent.click(screen.getByTestId('helloDiv'))
80
+ expect(posthog.capture).toHaveBeenCalledWith('$feature_interaction', {
81
+ feature_flag: 'test',
82
+ $set: { '$feature_interaction/test': true },
83
+ })
84
+ expect(posthog.capture).toHaveBeenCalledTimes(1)
85
+
86
+ fireEvent.click(screen.getByTestId('helloDiv'))
87
+ fireEvent.click(screen.getByTestId('helloDiv'))
88
+ fireEvent.click(screen.getByTestId('helloDiv'))
89
+ expect(posthog.capture).toHaveBeenCalledTimes(1)
90
+ })
91
+
92
+ it('should track an interaction with each child node of the feature component', () => {
93
+ render(
94
+ <PostHogProvider client={posthog}>
95
+ <PostHogFeature flag={'test'} match={true}>
96
+ <div data-testid="helloDiv">Hello</div>
97
+ <div data-testid="worldDiv">World!</div>
98
+ </PostHogFeature>
99
+ </PostHogProvider>
100
+ )
101
+
102
+ fireEvent.click(screen.getByTestId('helloDiv'))
103
+ fireEvent.click(screen.getByTestId('helloDiv'))
104
+ fireEvent.click(screen.getByTestId('worldDiv'))
105
+ fireEvent.click(screen.getByTestId('worldDiv'))
106
+ fireEvent.click(screen.getByTestId('worldDiv'))
107
+ expect(posthog.capture).toHaveBeenCalledWith('$feature_interaction', {
108
+ feature_flag: 'test',
109
+ $set: { '$feature_interaction/test': true },
110
+ })
111
+ expect(posthog.capture).toHaveBeenCalledTimes(1)
112
+ })
113
+
114
+ it('should not fire events when interaction is disabled', () => {
115
+ render(
116
+ <PostHogProvider client={posthog}>
117
+ <PostHogFeature flag={'test'} match={true} trackInteraction={false}>
118
+ <div data-testid="helloDiv">Hello</div>
119
+ </PostHogFeature>
120
+ </PostHogProvider>
121
+ )
122
+
123
+ fireEvent.click(screen.getByTestId('helloDiv'))
124
+ expect(posthog.capture).not.toHaveBeenCalled()
125
+
126
+ fireEvent.click(screen.getByTestId('helloDiv'))
127
+ fireEvent.click(screen.getByTestId('helloDiv'))
128
+ fireEvent.click(screen.getByTestId('helloDiv'))
129
+ expect(posthog.capture).not.toHaveBeenCalled()
130
+ })
131
+
132
+ it('should fire events when interaction is disabled but re-enabled after', () => {
133
+ const DynamicUpdateComponent = () => {
134
+ const [trackInteraction, setTrackInteraction] = useState(false)
135
+
136
+ return (
137
+ <>
138
+ <div
139
+ data-testid="clicker"
140
+ onClick={() => {
141
+ setTrackInteraction(true)
142
+ }}
143
+ >
144
+ Click me
145
+ </div>
146
+ <PostHogFeature flag={'test'} match={true} trackInteraction={trackInteraction}>
147
+ <div data-testid="helloDiv">Hello</div>
148
+ </PostHogFeature>
149
+ </>
150
+ )
151
+ }
152
+
153
+ render(
154
+ <PostHogProvider client={posthog}>
155
+ <DynamicUpdateComponent />
156
+ </PostHogProvider>
157
+ )
158
+
159
+ fireEvent.click(screen.getByTestId('helloDiv'))
160
+ expect(posthog.capture).not.toHaveBeenCalled()
161
+
162
+ fireEvent.click(screen.getByTestId('clicker'))
163
+ fireEvent.click(screen.getByTestId('helloDiv'))
164
+ fireEvent.click(screen.getByTestId('helloDiv'))
165
+ expect(posthog.capture).toHaveBeenCalledWith('$feature_interaction', {
166
+ feature_flag: 'test',
167
+ $set: { '$feature_interaction/test': true },
168
+ })
169
+ expect(posthog.capture).toHaveBeenCalledTimes(1)
170
+ })
171
+
172
+ it('should not show the feature component if the flag is not enabled', () => {
173
+ renderWith(posthog, 'test_value')
174
+
175
+ expect(screen.queryByTestId('helloDiv')).not.toBeInTheDocument()
176
+ expect(posthog.capture).not.toHaveBeenCalled()
177
+
178
+ // check if any elements are found
179
+ const allTags = screen.queryAllByText(/.*/)
180
+
181
+ // Assert that no random elements are found
182
+ expect(allTags.length).toEqual(2)
183
+ expect(allTags[0].tagName).toEqual('BODY')
184
+ expect(allTags[1].tagName).toEqual('DIV')
185
+ })
186
+
187
+ it('should fallback when provided', () => {
188
+ render(
189
+ <PostHogProvider client={posthog}>
190
+ <PostHogFeature flag={'test_false'} match={true} fallback={<div data-testid="nope">Nope</div>}>
191
+ <div data-testid="helloDiv">Hello</div>
192
+ </PostHogFeature>
193
+ </PostHogProvider>
194
+ )
195
+
196
+ expect(screen.queryByTestId('helloDiv')).not.toBeInTheDocument()
197
+ expect(posthog.capture).not.toHaveBeenCalled()
198
+
199
+ fireEvent.click(screen.getByTestId('nope'))
200
+ expect(posthog.capture).not.toHaveBeenCalled()
201
+ })
202
+
203
+ it('should handle showing multivariate flags with bool match', () => {
204
+ renderWith(posthog, 'multivariate_feature')
205
+
206
+ expect(screen.queryByTestId('helloDiv')).not.toBeInTheDocument()
207
+ expect(posthog.capture).not.toHaveBeenCalled()
208
+ })
209
+
210
+ it('should handle showing multivariate flags with incorrect match', () => {
211
+ renderWith(posthog, 'multivariate_feature', 'string-valueCXCC')
212
+
213
+ expect(screen.queryByTestId('helloDiv')).not.toBeInTheDocument()
214
+ expect(posthog.capture).not.toHaveBeenCalled()
215
+ })
216
+
217
+ it('should handle showing multivariate flags', () => {
218
+ renderWith(posthog, 'multivariate_feature', 'string-value')
219
+
220
+ expect(screen.queryByTestId('helloDiv')).toBeInTheDocument()
221
+ expect(posthog.capture).not.toHaveBeenCalled()
222
+
223
+ fireEvent.click(screen.getByTestId('helloDiv'))
224
+ expect(posthog.capture).toHaveBeenCalledWith('$feature_interaction', {
225
+ feature_flag: 'multivariate_feature',
226
+ feature_flag_variant: 'string-value',
227
+ $set: { '$feature_interaction/multivariate_feature': 'string-value' },
228
+ })
229
+ expect(posthog.capture).toHaveBeenCalledTimes(1)
230
+ })
231
+
232
+ it('should handle payload flags', () => {
233
+ render(
234
+ <PostHogProvider client={posthog}>
235
+ <PostHogFeature flag={'example_feature_payload'} match={'test'}>
236
+ {(payload: any) => {
237
+ return <div data-testid={`hi_${payload.name}`}>Hullo</div>
238
+ }}
239
+ </PostHogFeature>
240
+ </PostHogProvider>
241
+ )
242
+
243
+ expect(screen.queryByTestId('hi_example_feature_1_payload')).toBeInTheDocument()
244
+ expect(posthog.capture).not.toHaveBeenCalled()
245
+
246
+ fireEvent.click(screen.getByTestId('hi_example_feature_1_payload'))
247
+ expect(posthog.capture).toHaveBeenCalledTimes(1)
248
+ })
249
+ })
@@ -1,4 +1,5 @@
1
1
  export * from './PostHogFeature'
2
+ export * from './PostHogCaptureOnViewed'
2
3
  export {
3
4
  PostHogErrorBoundary,
4
5
  PostHogErrorBoundaryProps,
@@ -0,0 +1,49 @@
1
+ import React, { MouseEventHandler, useEffect, useMemo, useRef } from 'react'
2
+ import { isNull } from '../../utils/type-utils'
3
+
4
+ /**
5
+ * VisibilityAndClickTracker is an internal component,
6
+ * its API might change without warning and without being signalled as a breaking change
7
+ *
8
+ * Wraps the provided children in a div, and tracks visibility of and clicks on that div
9
+ */
10
+ export function VisibilityAndClickTracker({
11
+ children,
12
+ onIntersect,
13
+ onClick,
14
+ trackView,
15
+ options,
16
+ ...props
17
+ }: {
18
+ children: React.ReactNode
19
+ onIntersect: (entry: IntersectionObserverEntry) => void
20
+ onClick?: MouseEventHandler<HTMLDivElement>
21
+ trackView: boolean
22
+ options?: IntersectionObserverInit
23
+ }): JSX.Element {
24
+ const ref = useRef<HTMLDivElement>(null)
25
+
26
+ const observerOptions = useMemo(
27
+ () => ({
28
+ threshold: 0.1,
29
+ ...options,
30
+ }),
31
+ // eslint-disable-next-line react-hooks/exhaustive-deps
32
+ [options?.threshold, options?.root, options?.rootMargin]
33
+ )
34
+
35
+ useEffect(() => {
36
+ if (isNull(ref.current) || !trackView) return
37
+
38
+ // eslint-disable-next-line compat/compat
39
+ const observer = new IntersectionObserver(([entry]) => onIntersect(entry), observerOptions)
40
+ observer.observe(ref.current)
41
+ return () => observer.disconnect()
42
+ }, [observerOptions, trackView, onIntersect])
43
+
44
+ return (
45
+ <div ref={ref} {...props} onClick={onClick}>
46
+ {children}
47
+ </div>
48
+ )
49
+ }
@@ -0,0 +1,60 @@
1
+ import React, { Children, ReactNode, useCallback, useRef } from 'react'
2
+ import { VisibilityAndClickTracker } from './VisibilityAndClickTracker'
3
+
4
+ /**
5
+ * VisibilityAndClickTrackers is an internal component,
6
+ * its API might change without warning and without being signalled as a breaking change
7
+ *
8
+ * Wraps each of the children passed to it for visiblity and click tracking
9
+ *
10
+ */
11
+ export function VisibilityAndClickTrackers({
12
+ children,
13
+ trackInteraction,
14
+ trackView,
15
+ options,
16
+ onInteract,
17
+ onView,
18
+ ...props
19
+ }: {
20
+ flag: string
21
+ children: React.ReactNode
22
+ trackInteraction: boolean
23
+ trackView: boolean
24
+ options?: IntersectionObserverInit
25
+ onInteract?: () => void
26
+ onView?: () => void
27
+ }): JSX.Element {
28
+ const clickTrackedRef = useRef(false)
29
+ const visibilityTrackedRef = useRef(false)
30
+
31
+ const cachedOnClick = useCallback(() => {
32
+ if (!clickTrackedRef.current && trackInteraction && onInteract) {
33
+ onInteract()
34
+ clickTrackedRef.current = true
35
+ }
36
+ }, [trackInteraction, onInteract])
37
+
38
+ const onIntersect = (entry: IntersectionObserverEntry) => {
39
+ if (!visibilityTrackedRef.current && entry.isIntersecting && onView) {
40
+ onView()
41
+ visibilityTrackedRef.current = true
42
+ }
43
+ }
44
+
45
+ const trackedChildren = Children.map(children, (child: ReactNode) => {
46
+ return (
47
+ <VisibilityAndClickTracker
48
+ onClick={cachedOnClick}
49
+ onIntersect={onIntersect}
50
+ trackView={trackView}
51
+ options={options}
52
+ {...props}
53
+ >
54
+ {child}
55
+ </VisibilityAndClickTracker>
56
+ )
57
+ })
58
+
59
+ return <>{trackedChildren}</>
60
+ }
@@ -1,30 +1,31 @@
1
1
  import * as React from 'react'
2
2
  import { render } from '@testing-library/react'
3
- import { PostHogProvider } from '..'
3
+ import { PostHogProvider, PostHog } from '..'
4
4
 
5
5
  describe('PostHogContext component', () => {
6
- given(
7
- 'render',
8
- () => () =>
9
- render(
10
- <PostHogProvider client={given.posthog}>
11
- <div>Hello</div>
12
- </PostHogProvider>
13
- )
14
- )
15
- given('posthog', () => ({}))
6
+ const posthog = {} as unknown as PostHog
16
7
 
17
8
  it('should return a client instance from the context if available', () => {
18
- given.render()
9
+ render(
10
+ <PostHogProvider client={posthog}>
11
+ <div>Hello</div>
12
+ </PostHogProvider>
13
+ )
19
14
  })
20
15
 
21
16
  it("should not throw error if a client instance can't be found in the context", () => {
22
- given('posthog', () => undefined) // it might not exist in SSR for example
23
-
24
17
  // eslint-disable-next-line no-console
25
18
  console.warn = jest.fn()
26
19
 
27
- expect(() => given.render()).not.toThrow()
20
+ expect(() => {
21
+ render(
22
+ // we have to cast `as any` so that we can test for when
23
+ // posthog might not exist - in SSR for example
24
+ <PostHogProvider client={undefined as any}>
25
+ <div>Hello</div>
26
+ </PostHogProvider>
27
+ )
28
+ }).not.toThrow()
28
29
 
29
30
  // eslint-disable-next-line no-console
30
31
  expect(console.warn).toHaveBeenCalledWith(
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react'
2
2
  import { render, act } from '@testing-library/react'
3
- import { PostHogProvider } from '..'
3
+ import { PostHogProvider, PostHog } from '..'
4
4
  import posthogJs from 'posthog-js'
5
5
 
6
6
  // Mock posthog-js
@@ -15,7 +15,7 @@ jest.mock('posthog-js', () => ({
15
15
 
16
16
  describe('PostHogProvider component', () => {
17
17
  it('should render children components', () => {
18
- const posthog = {}
18
+ const posthog = {} as unknown as PostHog
19
19
  const { getByText } = render(
20
20
  <PostHogProvider client={posthog}>
21
21
  <div>Test</div>
@@ -111,7 +111,7 @@ describe('PostHogProvider component', () => {
111
111
  })
112
112
 
113
113
  it('warns if posthogJs has been loaded elsewhere', () => {
114
- posthogJs.__loaded = true // Pretend it's initialized
114
+ ;(posthogJs as any).__loaded = true
115
115
 
116
116
  const consoleSpy = jest.spyOn(console, 'warn').mockImplementation()
117
117
  render(
@@ -125,7 +125,7 @@ describe('PostHogProvider component', () => {
125
125
  )
126
126
 
127
127
  consoleSpy.mockRestore()
128
- posthogJs.__loaded = false
128
+ ;(posthogJs as any).__loaded = false
129
129
  })
130
130
  })
131
131
  })
@@ -1,20 +1,20 @@
1
1
  import * as React from 'react'
2
2
  import { renderHook } from '@testing-library/react-hooks'
3
- import { PostHogProvider } from '../../context'
3
+ import { PostHogProvider, PostHog } from '../../context'
4
4
  import { useFeatureFlagPayload, useFeatureFlagVariantKey, useFeatureFlagEnabled, useActiveFeatureFlags } from '../index'
5
5
 
6
6
  jest.useFakeTimers()
7
7
 
8
8
  const ACTIVE_FEATURE_FLAGS = ['example_feature_true', 'multivariate_feature', 'example_feature_payload']
9
9
 
10
- const FEATURE_FLAG_STATUS = {
10
+ const FEATURE_FLAG_STATUS: Record<string, string | boolean> = {
11
11
  example_feature_true: true,
12
12
  example_feature_false: false,
13
13
  multivariate_feature: 'string-value',
14
14
  example_feature_payload: 'test',
15
15
  }
16
16
 
17
- const FEATURE_FLAG_PAYLOADS = {
17
+ const FEATURE_FLAG_PAYLOADS: Record<string, any> = {
18
18
  example_feature_payload: {
19
19
  id: 1,
20
20
  name: 'example_feature_1_payload',
@@ -23,28 +23,32 @@ const FEATURE_FLAG_PAYLOADS = {
23
23
  }
24
24
 
25
25
  describe('useFeatureFlagPayload hook', () => {
26
- given('renderProvider', () => ({ children }) => (
27
- <PostHogProvider client={given.posthog}>{children}</PostHogProvider>
28
- ))
26
+ let posthog: PostHog
27
+ let renderProvider: React.FC<{ children: React.ReactNode }>
29
28
 
30
- given('posthog', () => ({
31
- isFeatureEnabled: (flag) => !!FEATURE_FLAG_STATUS[flag],
32
- getFeatureFlag: (flag) => FEATURE_FLAG_STATUS[flag],
33
- getFeatureFlagPayload: (flag) => FEATURE_FLAG_PAYLOADS[flag],
34
- onFeatureFlags: (callback) => {
35
- const activeFlags = []
36
- for (const flag in FEATURE_FLAG_STATUS) {
37
- if (FEATURE_FLAG_STATUS[flag]) {
38
- activeFlags.push(flag)
29
+ beforeEach(() => {
30
+ posthog = {
31
+ isFeatureEnabled: (flag: string) => !!FEATURE_FLAG_STATUS[flag],
32
+ getFeatureFlag: (flag: string) => FEATURE_FLAG_STATUS[flag],
33
+ getFeatureFlagPayload: (flag: string) => FEATURE_FLAG_PAYLOADS[flag],
34
+ onFeatureFlags: (callback: any) => {
35
+ const activeFlags: string[] = []
36
+ for (const flag in FEATURE_FLAG_STATUS) {
37
+ if (FEATURE_FLAG_STATUS[flag]) {
38
+ activeFlags.push(flag)
39
+ }
39
40
  }
40
- }
41
- callback(activeFlags)
42
- return () => {}
43
- },
44
- featureFlags: {
45
- getFlags: () => ACTIVE_FEATURE_FLAGS,
46
- },
47
- }))
41
+ callback(activeFlags)
42
+ return () => {}
43
+ },
44
+ featureFlags: {
45
+ getFlags: () => ACTIVE_FEATURE_FLAGS,
46
+ } as unknown as PostHog['featureFlags'],
47
+ } as unknown as PostHog
48
+
49
+ // eslint-disable-next-line react/display-name
50
+ renderProvider = ({ children }) => <PostHogProvider client={posthog}>{children}</PostHogProvider>
51
+ })
48
52
 
49
53
  it.each([
50
54
  ['example_feature_true', true],
@@ -53,8 +57,8 @@ describe('useFeatureFlagPayload hook', () => {
53
57
  ['multivariate_feature', true],
54
58
  ['example_feature_payload', true],
55
59
  ])('should get the boolean feature flag', (flag, expected) => {
56
- let { result } = renderHook(() => useFeatureFlagEnabled(flag), {
57
- wrapper: given.renderProvider,
60
+ const { result } = renderHook(() => useFeatureFlagEnabled(flag), {
61
+ wrapper: renderProvider,
58
62
  })
59
63
  expect(result.current).toEqual(expected)
60
64
  })
@@ -66,15 +70,15 @@ describe('useFeatureFlagPayload hook', () => {
66
70
  ['multivariate_feature', undefined],
67
71
  ['example_feature_payload', FEATURE_FLAG_PAYLOADS.example_feature_payload],
68
72
  ])('should get the payload feature flag', (flag, expected) => {
69
- let { result } = renderHook(() => useFeatureFlagPayload(flag), {
70
- wrapper: given.renderProvider,
73
+ const { result } = renderHook(() => useFeatureFlagPayload(flag), {
74
+ wrapper: renderProvider,
71
75
  })
72
76
  expect(result.current).toEqual(expected)
73
77
  })
74
78
 
75
79
  it('should return the active feature flags', () => {
76
- let { result } = renderHook(() => useActiveFeatureFlags(), {
77
- wrapper: given.renderProvider,
80
+ const { result } = renderHook(() => useActiveFeatureFlags(), {
81
+ wrapper: renderProvider,
78
82
  })
79
83
  expect(result.current).toEqual(['example_feature_true', 'multivariate_feature', 'example_feature_payload'])
80
84
  })
@@ -85,8 +89,8 @@ describe('useFeatureFlagPayload hook', () => {
85
89
  ['missing', undefined],
86
90
  ['multivariate_feature', 'string-value'],
87
91
  ])('should get the feature flag variant key', (flag, expected) => {
88
- let { result } = renderHook(() => useFeatureFlagVariantKey(flag), {
89
- wrapper: given.renderProvider,
92
+ const { result } = renderHook(() => useFeatureFlagVariantKey(flag), {
93
+ wrapper: renderProvider,
90
94
  })
91
95
  expect(result.current).toEqual(expected)
92
96
  })