Skip to content

Commit

Permalink
feat: Fire Pendo event on RepoPage for JS and TS users (#3158)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholas-codecov authored Sep 6, 2024
1 parent e6a4224 commit 68a1e3a
Show file tree
Hide file tree
Showing 8 changed files with 426 additions and 15 deletions.
18 changes: 18 additions & 0 deletions src/App.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,21 @@ const user = {
},
}

const mockRepoOverview = {
owner: {
repository: {
__typename: 'Repository',
private: false,
defaultBranch: 'main',
oldestCommitAt: '2022-10-10T11:59:59',
coverageEnabled: true,
bundleAnalysisEnabled: true,
testAnalyticsEnabled: true,
languages: ['JavaScript'],
},
},
}

const queryClient = new QueryClient({
defaultOptions: {
queries: {
Expand Down Expand Up @@ -196,6 +211,9 @@ describe('App', () => {
}),
graphql.mutation('updateDefaultOrganization', (req, res, ctx) => {
return res(ctx.status(200), ctx.data({}))
}),
graphql.query('GetRepoOverview', (req, res, ctx) => {
return res(ctx.status(200), ctx.data(mockRepoOverview))
})
)
}
Expand Down
58 changes: 58 additions & 0 deletions src/pages/RepoPage/RepoPage.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,58 @@ const mockRepoOverview = ({
}
}

const mockOwner = {
ownerid: 123,
username: 'test-owner',
avatarUrl: 'http://127.0.0.1/avatar-url',
isCurrentUserPartOfOrg: true,
isAdmin: true,
}

const mockUser = {
me: {
owner: {
defaultOrgUsername: 'codecov',
},
email: '[email protected]',
privateAccess: true,
onboardingCompleted: true,
businessEmail: '[email protected]',
termsAgreement: true,
user: {
name: 'Jane Doe',
username: 'janedoe',
avatarUrl: 'http://127.0.0.1/avatar-url',
avatar: 'http://127.0.0.1/avatar-url',
student: false,
studentCreatedAt: null,
studentUpdatedAt: null,
customerIntent: 'PERSONAL',
},
trackingMetadata: {
service: 'github',
ownerid: 123,
serviceId: '123',
plan: 'users-basic',
staff: false,
hasYaml: false,
bot: null,
delinquent: null,
didTrial: null,
planProvider: null,
planUserCount: 1,
createdAt: 'timestamp',
updatedAt: 'timestamp',
profile: {
createdAt: 'timestamp',
otherGoal: null,
typeProjects: [],
goals: [],
},
},
},
}

const server = setupServer()
let testLocation: ReturnType<typeof useLocation>

Expand Down Expand Up @@ -245,6 +297,12 @@ describe('RepoPage', () => {
})
)
)
}),
graphql.query('DetailOwner', (req, res, ctx) => {
return res(ctx.data({ owner: mockOwner }))
}),
graphql.query('CurrentUser', (req, res, ctx) => {
return res(ctx.status(200), ctx.data(mockUser))
})
)

Expand Down
3 changes: 3 additions & 0 deletions src/pages/RepoPage/RepoPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import LoadingLogo from 'ui/LoadingLogo'
import ActivationAlert from './ActivationAlert'
import { useCrumbs } from './context'
import DeactivatedRepo from './DeactivatedRepo'
import { useJSorTSPendoTracking } from './hooks/useJSorTSPendoTracking'
import RepoPageTabs from './RepoPageTabs'

const BundlesTab = lazy(() => import('./BundlesTab'))
Expand Down Expand Up @@ -252,6 +253,8 @@ function RepoPage() {
})
const { setBaseCrumbs } = useCrumbs()

useJSorTSPendoTracking()

useLayoutEffect(() => {
setBaseCrumbs([
{ pageName: 'owner', text: owner },
Expand Down
257 changes: 257 additions & 0 deletions src/pages/RepoPage/hooks/useJSorTSPendoTracking.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { renderHook, screen, waitFor } from '@testing-library/react'
import { userEvent } from '@testing-library/user-event'
import { graphql } from 'msw'
import { setupServer } from 'msw/node'
import { Link, MemoryRouter, Route } from 'react-router-dom'

import { useJSorTSPendoTracking } from './useJSorTSPendoTracking'

const mockUser = {
me: {
owner: {
defaultOrgUsername: 'codecov',
},
email: '[email protected]',
privateAccess: true,
onboardingCompleted: true,
businessEmail: '[email protected]',
termsAgreement: true,
user: {
name: 'Jane Doe',
username: 'janedoe',
avatarUrl: 'http://127.0.0.1/avatar-url',
avatar: 'http://127.0.0.1/avatar-url',
student: false,
studentCreatedAt: null,
studentUpdatedAt: null,
customerIntent: 'PERSONAL',
},
trackingMetadata: {
service: 'github',
ownerid: 123,
serviceId: '123',
plan: 'users-basic',
staff: false,
hasYaml: false,
bot: null,
delinquent: null,
didTrial: null,
planProvider: null,
planUserCount: 1,
createdAt: 'timestamp',
updatedAt: 'timestamp',
profile: {
createdAt: 'timestamp',
otherGoal: null,
typeProjects: [],
goals: [],
},
},
},
}

const mockOverview = (language: string) => ({
owner: {
isCurrentUserActivated: true,
repository: {
__typename: 'Repository',
private: false,
defaultBranch: 'main',
oldestCommitAt: '2022-10-10T11:59:59',
coverageEnabled: true,
bundleAnalysisEnabled: true,
testAnalyticsEnabled: true,
languages: [language],
},
},
})

const mockOwner1 = {
ownerid: 123,
username: 'test-owner',
avatarUrl: 'http://127.0.0.1/avatar-url',
isCurrentUserPartOfOrg: true,
isAdmin: true,
}

const mockOwner2 = {
ownerid: 456,
username: 'second-owner',
avatarUrl: 'http://127.0.0.1/avatar-url',
isCurrentUserPartOfOrg: true,
isAdmin: true,
}

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
})

const wrapper: React.FC<React.PropsWithChildren> = ({ children }) => (
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={['/github/test-owner/test-repo']}>
<Route path="/:provider/:owner/:repo">
{children}
<Link to="/:provider/second-owner/second-repo">ClickMe</Link>
</Route>
</MemoryRouter>
</QueryClientProvider>
)

const server = setupServer()
beforeAll(() => {
server.listen()
})

afterEach(() => {
queryClient.clear()
server.resetHandlers()
})

afterAll(() => {
server.close()
})

interface SetupArgs {
enablePendo?: boolean
language?: string
}

describe('useJSorTSPendoTracking', () => {
function setup({ enablePendo = false, language = 'javascript' }: SetupArgs) {
server.use(
graphql.query('CurrentUser', (req, res, ctx) => {
return res(ctx.status(200), ctx.data(mockUser))
}),
graphql.query('GetRepoOverview', (req, res, ctx) => {
return res(ctx.status(200), ctx.data(mockOverview(language)))
}),
graphql.query('DetailOwner', (req, res, ctx) => {
if (req.variables.username === 'second-owner') {
return res(
ctx.status(200),
ctx.data({
owner: mockOwner2,
})
)
}

return res(
ctx.status(200),
ctx.data({
owner: mockOwner1,
})
)
})
)

const updateOptionsMock = jest.fn()
if (enablePendo) {
window.pendo = {
initialize: jest.fn(),
updateOptions: updateOptionsMock,
}
} else {
window.pendo = {}
}

const user = userEvent.setup()

return { updateOptionsMock, user }
}

describe('js or ts is present', () => {
describe('first render', () => {
it('fires the event setting value to true', async () => {
const { updateOptionsMock } = setup({
enablePendo: true,
language: 'javascript',
})

renderHook(() => useJSorTSPendoTracking(), { wrapper })

await waitFor(() =>
expect(updateOptionsMock).toHaveBeenCalledWith({
account: expect.objectContaining({
id: 123,
name: 'test-owner',
}),
visitor: expect.objectContaining({
js_or_ts_present: true,
}),
})
)
})
})

describe('repo has changed', () => {
it('fires the event a second time setting value to true', async () => {
const { updateOptionsMock, user } = setup({
enablePendo: true,
language: 'javascript',
})

renderHook(() => useJSorTSPendoTracking(), { wrapper })

const link = screen.getByText('ClickMe')
await user.click(link)

await waitFor(() => expect(updateOptionsMock).toHaveBeenCalled())
await waitFor(() =>
expect(updateOptionsMock).toHaveBeenCalledWith({
account: expect.objectContaining({
id: 456,
name: 'second-owner',
}),
visitor: expect.objectContaining({
js_or_ts_present: true,
}),
})
)
})
})
})

describe('js or ts is not present', () => {
it('fires the event setting value to false', async () => {
const { updateOptionsMock } = setup({
enablePendo: true,
language: 'python',
})

renderHook(() => useJSorTSPendoTracking(), { wrapper })

await waitFor(() =>
expect(updateOptionsMock).toHaveBeenCalledWith({
account: expect.objectContaining({
id: 123,
name: 'test-owner',
}),
visitor: expect.objectContaining({
js_or_ts_present: false,
}),
})
)
})
})

describe('pendo is not present in the window', () => {
it('does not call pendo.updateOptions', async () => {
const { updateOptionsMock } = setup({
enablePendo: false,
language: 'javascript',
})

renderHook(() => useJSorTSPendoTracking(), { wrapper })

await waitFor(() => queryClient.isFetching)
await waitFor(() => !queryClient.isFetching)

await waitFor(() => expect(updateOptionsMock).not.toHaveBeenCalled())
})
})
})
Loading

0 comments on commit 68a1e3a

Please sign in to comment.