Skip to content

Commit

Permalink
iteration 1
Browse files Browse the repository at this point in the history
  • Loading branch information
Bianca Yang committed Mar 8, 2024
1 parent 7ede719 commit b8a506c
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 57 deletions.
170 changes: 114 additions & 56 deletions cypress/e2e/onboarding.cy.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { urls } from 'scenes/urls'
import { decideResponse } from '../fixtures/api/decide'

describe('Onboarding', () => {
beforeEach(() => {
cy.intercept('https://us.i.posthog.com/decide/*', (req) =>
req.reply(
decideResponse({
'product-intro-pages': 'test',
})
)
)
cy.intercept('https://app.posthog.com/decide/*', (req) =>
req.reply(
decideResponse({
Expand All @@ -12,86 +18,138 @@ describe('Onboarding', () => {
)
})

// it('Navigate between /products to /onboarding to a product intro page', () => {
// cy.visit('/products')
it('Navigate between /products to /onboarding to a product intro page', () => {
cy.visit('/products')

// Get started on product analytics onboarding
cy.get('[data-attr=product_analytics-onboarding-card]').click()

// Confirm product intro is not included as the first step in the upper right breadcrumbs
cy.get('[data-attr=onboarding-breadcrumbs] > :first-child > * span').should('not.contain', 'Product Intro')

// Get started on product analytics onboarding
// cy.get('[data-attr=product_analytics-onboarding-card]').click()
// Navigate to the product intro page by clicking the left side bar
cy.get('[data-attr=menu-item-replay').click()

// // Confirm product intro is not included as the first step in the upper right breadcrumbs
// cy.get('[data-attr=onboarding-breadcrumbs] > :first-child > * span').should('not.contain', 'Product Intro')
// Confirm we're on the product_intro page
cy.get('[data-attr=top-bar-name] > span').contains('Product intro')

// // Navigate to the product intro page by clicking the left side bar
// cy.get('[data-attr=menu-item-replay').click()
// Go back to /products
cy.visit('/products')

// // Confirm we're on the product_intro page
// cy.get('[data-attr=top-bar-name] > span').contains('Product intro')
// Again get started on product analytics onboarding
cy.get('[data-attr=product_analytics-get-started-button]').click()

// // Go back to /products
// cy.visit('/products')
// Navigate to the product intro page by changing the url
cy.visit(urls.onboarding('session_replay', 'product_intro'))

// // Again get started on product analytics onboarding
// cy.get('[data-attr=product_analytics-get-started-button]').click()
// Confirm we're on the product intro page
cy.get('[data-attr=top-bar-name] > span').contains('Product intro')
})

it('Step through PA onboarding', () => {
cy.visit('/products')

// // Navigate to the product intro page by changing the url
// cy.visit(urls.onboarding('session_replay', 'product_intro'))
// Get started on product analytics onboarding
cy.get('[data-attr=product_analytics-onboarding-card]').click()

// // Confirm we're on the product intro page
// cy.get('[data-attr=top-bar-name] > span').contains('Product intro')
// })
// Installation should be complete
cy.get('svg.LemonIcon.text-success').should('exist')
cy.get('svg.LemonIcon.text-success').parent().should('contain', 'Installation complete')

// it('Step through PA onboarding', () => {
// cy.visit('/products')
// Continue to configuration step
cy.get('[data-attr=sdk-continue]').click()

// Get started on product analytics onboarding
// cy.get('[data-attr=product_analytics-onboarding-card]').click()
// Confirm the appropriate breadcrumb is highlighted
cy.get('[data-attr=onboarding-breadcrumbs] > :nth-child(3) > * span').should('contain', 'Configure')
cy.get('[data-attr=onboarding-breadcrumbs] > :nth-child(3) > * span').should('not.have.css', 'text-muted')

// // Installation should be complete
// cy.get('svg.LemonIcon.text-success').should('exist')
// cy.get('svg.LemonIcon.text-success').parent().should('contain', 'Installation complete')
// Continue to plans
cy.get('[data-attr=onboarding-continue]').click()

// // Continue to configuration step
// cy.get('[data-attr=sdk-continue]').click()
// Click show plans
cy.get('[data-attr=show-plans]').click()

// // Confirm the appropriate breadcrumb is highlighted
// cy.get('[data-attr=onboarding-breadcrumbs] > :nth-child(3) > * span').should('contain', 'Configure')
// cy.get('[data-attr=onboarding-breadcrumbs] > :nth-child(3) > * span').should('not.have.css', 'text-muted')
// Verify pricing table visible
cy.get('.BillingHero').should('be.visible')
cy.get('table.PlanComparison').should('be.visible')

// // Continue to plans
// cy.get('[data-attr=onboarding-continue]').click()
// Continue
cy.get('[data-attr=onboarding-continue]').click()

// // Click show plans
// cy.get('[data-attr=show-plans').click()
// Click back to Install step
cy.get('[data-attr=onboarding-breadcrumbs] > :first-child > * span').click()

// // Verify pricing table visible
// cy.get('.BillingHero').should('be.visible')
// cy.get('table.PlanComparison').should('be.visible')
// Continue through to finish
cy.get('[data-attr=sdk-continue]').click()
cy.get('[data-attr=onboarding-continue]').click()
cy.get('[data-attr=onboarding-continue]').click()
cy.get('[data-attr=onboarding-continue]').click()

// // Continue
// cy.get('[data-attr=onboarding-continue]').click()
// Confirm we're on the insights list page
cy.url().should('contain', 'http://localhost:8080/project/1/insights')

// // Click back to Install step
// cy.get('[data-attr=onboarding-breadcrumbs] > :first-child > * span').click()
cy.visit('/onboarding/product_analytics?step=product_intro')

// // Continue through to finish
// cy.get('[data-attr=sdk-continue]').click()
// cy.get('[data-attr=onboarding-continue]').click()
// cy.get('[data-attr=onboarding-continue]').click()
// cy.get('[data-attr=onboarding-continue]').click()
// Should see both an option to skip onboarding and an option to see the sdk instructions
cy.get('[data-attr=skip-onboarding]').should('be.visible')
cy.get('[data-attr=start-onboarding-sdk]').should('be.visible')

// // Confirm we're on the insights list page
// cy.url().should('eq', 'https://localhost:8080/project/1/insights')
// })
cy.get('[data-attr=skip-onboarding]').first().click()
cy.url().should('contain', 'http://localhost:8080/project/1/insights')

cy.visit('/onboarding/product_analytics?step=product_intro')
cy.get('[data-attr=start-onboarding-sdk]').first().click()
cy.url().should('contain', 'http://localhost:8080/project/1/onboarding/product_analytics?step=install')
})

it('Step through SR onboarding', () => {
cy.visit('/replay')
cy.get('[data-attr=menu-item-replay]').click()
cy.get('[data-attr=start-onboarding]').first().click()
// Installation should be complete
cy.get('svg.LemonIcon.text-success').should('exist')
cy.get('svg.LemonIcon.text-success').parent().should('contain', 'Installation complete')
// Continue to configuration step
cy.get('[data-attr=sdk-continue]').click()
// Continue to plans
cy.get('[data-attr=onboarding-continue]').click()
// Go back to intro page
cy.visit('/onboarding/session_replay?step=product_intro')
// Continue through to finish
cy.get('[data-attr=start-onboarding]').first().click()
cy.get('[data-attr=sdk-continue]').click()
cy.get('[data-attr=onboarding-continue]').click()
cy.get('[data-attr=onboarding-continue]').click()
// Confirm we're on the recordings list page
cy.url().should('eq', 'https://localhost:8080/project/1/replay/recent')

cy.visit('/onboarding/session_replay?step=product_intro')
cy.get('[data-attr=skip-onboarding]').should('be.visible')
cy.get('[data-attr=start-onboarding-sdk]').should('not.exist')
})

// it('Step through FF onboarding', () => {})
it('Step through FF onboarding', () => {
cy.visit('/onboarding/feature_flags?step=product_intro')
cy.get('[data-attr=menu-item-featureflags]').click()
cy.get('[data-attr=start-onboarding-sdk]').first().click()
cy.get('[data-attr=sdk-continue]').click()

// it('Step through Surveys onboarding', () => {})
// Confirm the appropriate breadcrumb is highlighted
cy.get('[data-attr=onboarding-breadcrumbs] > :nth-child(5) > * span').should('contain', 'Invite teammates')

// it('Click through product intro pages', () => {})
cy.visit('/onboarding/feature_flags?step=product_intro')

// it('Product intro pages to onboarding flow', () => {})
cy.get('[data-attr=skip-onboarding]').should('be.visible')
cy.get('[data-attr=start-onboarding-sdk]').should('be.visible')

cy.get('[data-attr=skip-onboarding]').click()
cy.url().should('contain', 'feature_flags/new')
})

it('Step through Surveys onboarding', () => {
cy.visit('/onboarding/surveys?step=product_intro')
cy.get('[data-attr=skip-onboarding]').should('be.visible')
cy.get('[data-attr=start-onboarding-sdk]').should('not.exist')
cy.get('[data-attr=skip-onboarding]').first().click()
cy.url().should('contain', 'survey_templates')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const GetStartedButton = ({ product }: { product: BillingProductV2Type }): JSX.E
{(!hasSnippetEvents || multiInstallProducts.includes(product.type as ProductKey)) && (
<LemonButton
type="tertiary"
data-attr="start-onboarding"
data-attr="start-onboarding-sdk"
onClick={() => {
setTeamPropertiesForProduct(product.type as ProductKey)
reportOnboardingProductSelected(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { afterMount, kea, key, path, props, reducers, selectors } from 'kea'
import { loaders } from 'kea-loaders'
import { subscriptions } from 'kea-subscriptions'
import api from 'lib/api'
import posthog from 'posthog-js'

import { HogQLQuery, NodeKind } from '~/queries/schema'
import { hogql } from '~/queries/utils'

import type { androidRecordingPromptBannerLogicType } from './androidRecordingPromptBannerLogicType'

export interface AndroidRecordingPromptBannerLogicProps {
context: 'home' | 'events' | 'replay'
}

export type AndroidEventCount = {
version: string
count?: number
}

export const androidRecordingPromptBannerLogic = kea<androidRecordingPromptBannerLogicType>([
path(['scenes', 'session-recordings', 'SessionRecordings']),
key((props) => props.context),
props({} as AndroidRecordingPromptBannerLogicProps),
loaders(({ values }) => ({
androidVersions: [
[] as AndroidEventCount[],
{
loadAndroidLibVersions: async () => {
if (values.androidVersions && values.androidVersions.length > 0) {
// if we know they ever had android events, don't check again
return values.androidVersions
}

const query: HogQLQuery = {
kind: NodeKind.HogQLQuery,
query: hogql`SELECT properties.$lib_version AS lib_version,
max(timestamp) AS latest_timestamp,
count(lib_version) as count
FROM events
WHERE timestamp >= now() - INTERVAL 30 DAY
AND timestamp <= now()
AND properties.$lib = 'posthog-android'
GROUP BY lib_version
ORDER BY latest_timestamp DESC
limit 10`,
}

const res = await api.query(query)

return (
res.results?.map((x) => ({
version: x[0],
count: x[2],
})) ?? []
)
},
},
],
})),
reducers({
androidVersions: [
// as a reducer only so we can persist it
[] as AndroidEventCount[],
{ persist: true },
{
loadAndroidLibVersionsSuccess: (_, { androidVersions }) => {
return androidVersions ?? []
},
},
],
}),

selectors({
shouldPromptUser: [
(s) => [s.androidVersions],
(androidVersions) => {
return (androidVersions?.length || 0) > 0
},
],
}),

subscriptions(({ values, props }) => ({
shouldPromptUser: (value, oldvalue) => {
// not a falsy check since we don't care when oldvalue is undefined
// we don't need multiple copies of this event so try to only emit it when `shouldPromptUser` changes to true
if (value === true && oldvalue === false) {
posthog.capture('visitor has android events', {
androidVersions: values.androidVersions,
scene: props.context,
})
}
},
})),

afterMount(({ actions }) => {
actions.loadAndroidLibVersions()
}),
])

0 comments on commit b8a506c

Please sign in to comment.