mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
feat: Add telemetry tracking to DismissibleUpsell component (#8309)
* feat: add telemetry tracking to DismissibleUpsell component - Added UPSELL_DISMISSED and UPSELL_CLICKED events to TelemetryEventName enum - Updated DismissibleUpsell component to track clicks and dismissals with telemetry - Added telemetry tests to DismissibleUpsell test suite - Events include upsellId in the payload for tracking specific upsells * refactor(webview): make handleDismiss synchronous in DismissibleUpsell test(webview): add scenario where dismissOnClick=true without onClick tracks only UPSELL_DISMISSED; update tests; all tests passing locally --------- Co-authored-by: Roo Code <roomote@roocode.com>
This commit is contained in:
parent
43c1de0d7e
commit
d959e88a97
3 changed files with 91 additions and 4 deletions
|
|
@ -61,6 +61,9 @@ export enum TelemetryEventName {
|
|||
ACCOUNT_LOGOUT_CLICKED = "Account Logout Clicked",
|
||||
ACCOUNT_LOGOUT_SUCCESS = "Account Logout Success",
|
||||
|
||||
UPSELL_DISMISSED = "Upsell Dismissed",
|
||||
UPSELL_CLICKED = "Upsell Clicked",
|
||||
|
||||
SCHEMA_VALIDATION_ERROR = "Schema Validation Error",
|
||||
DIFF_APPLICATION_ERROR = "Diff Application Error",
|
||||
SHELL_INTEGRATION_ERROR = "Shell Integration Error",
|
||||
|
|
@ -181,6 +184,8 @@ export const rooCodeTelemetryEventSchema = z.discriminatedUnion("type", [
|
|||
TelemetryEventName.ACCOUNT_CONNECT_SUCCESS,
|
||||
TelemetryEventName.ACCOUNT_LOGOUT_CLICKED,
|
||||
TelemetryEventName.ACCOUNT_LOGOUT_SUCCESS,
|
||||
TelemetryEventName.UPSELL_DISMISSED,
|
||||
TelemetryEventName.UPSELL_CLICKED,
|
||||
TelemetryEventName.SCHEMA_VALIDATION_ERROR,
|
||||
TelemetryEventName.DIFF_APPLICATION_ERROR,
|
||||
TelemetryEventName.SHELL_INTEGRATION_ERROR,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { memo, ReactNode, useEffect, useState, useRef } from "react"
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { telemetryClient } from "@src/utils/TelemetryClient"
|
||||
import { TelemetryEventName } from "@roo-code/types"
|
||||
|
||||
interface DismissibleUpsellProps {
|
||||
/** Required unique identifier for this upsell */
|
||||
|
|
@ -76,7 +78,12 @@ const DismissibleUpsell = memo(
|
|||
}
|
||||
}, [upsellId])
|
||||
|
||||
const handleDismiss = async () => {
|
||||
const handleDismiss = () => {
|
||||
// Track telemetry for dismissal
|
||||
telemetryClient.capture(TelemetryEventName.UPSELL_DISMISSED, {
|
||||
upsellId: upsellId,
|
||||
})
|
||||
|
||||
// First notify the extension to persist the dismissal
|
||||
// This ensures the message is sent even if the component unmounts quickly
|
||||
vscode.postMessage({
|
||||
|
|
@ -134,6 +141,13 @@ const DismissibleUpsell = memo(
|
|||
<div
|
||||
className={containerClasses}
|
||||
onClick={() => {
|
||||
// Track telemetry for click
|
||||
if (onClick) {
|
||||
telemetryClient.capture(TelemetryEventName.UPSELL_CLICKED, {
|
||||
upsellId: upsellId,
|
||||
})
|
||||
}
|
||||
|
||||
// Call the onClick handler if provided
|
||||
onClick?.()
|
||||
// Also dismiss if dismissOnClick is true
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { render, screen, fireEvent, waitFor, act } from "@testing-library/react"
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"
|
||||
import DismissibleUpsell from "../DismissibleUpsell"
|
||||
import { TelemetryEventName } from "@roo-code/types"
|
||||
|
||||
// Mock the vscode API
|
||||
const mockPostMessage = vi.fn()
|
||||
|
|
@ -10,6 +11,14 @@ vi.mock("@src/utils/vscode", () => ({
|
|||
},
|
||||
}))
|
||||
|
||||
// Mock telemetryClient
|
||||
const mockCapture = vi.fn()
|
||||
vi.mock("@src/utils/TelemetryClient", () => ({
|
||||
telemetryClient: {
|
||||
capture: (eventName: string, properties?: Record<string, any>) => mockCapture(eventName, properties),
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock the translation hook
|
||||
vi.mock("@src/i18n/TranslationContext", () => ({
|
||||
useAppTranslation: () => ({
|
||||
|
|
@ -26,6 +35,7 @@ vi.mock("@src/i18n/TranslationContext", () => ({
|
|||
describe("DismissibleUpsell", () => {
|
||||
beforeEach(() => {
|
||||
mockPostMessage.mockClear()
|
||||
mockCapture.mockClear()
|
||||
vi.clearAllTimers()
|
||||
})
|
||||
|
||||
|
|
@ -72,7 +82,7 @@ describe("DismissibleUpsell", () => {
|
|||
})
|
||||
})
|
||||
|
||||
it("hides the upsell when dismiss button is clicked", async () => {
|
||||
it("hides the upsell when dismiss button is clicked and tracks telemetry", async () => {
|
||||
const onDismiss = vi.fn()
|
||||
const { container } = render(
|
||||
<DismissibleUpsell upsellId="test-upsell" onDismiss={onDismiss}>
|
||||
|
|
@ -92,6 +102,11 @@ describe("DismissibleUpsell", () => {
|
|||
const dismissButton = screen.getByRole("button", { name: /dismiss/i })
|
||||
fireEvent.click(dismissButton)
|
||||
|
||||
// Check that telemetry was tracked
|
||||
expect(mockCapture).toHaveBeenCalledWith(TelemetryEventName.UPSELL_DISMISSED, {
|
||||
upsellId: "test-upsell",
|
||||
})
|
||||
|
||||
// Check that the dismiss message was sent BEFORE hiding
|
||||
expect(mockPostMessage).toHaveBeenCalledWith({
|
||||
type: "dismissUpsell",
|
||||
|
|
@ -351,7 +366,7 @@ describe("DismissibleUpsell", () => {
|
|||
})
|
||||
})
|
||||
|
||||
it("calls onClick when the container is clicked", async () => {
|
||||
it("calls onClick when the container is clicked and tracks telemetry", async () => {
|
||||
const onClick = vi.fn()
|
||||
render(
|
||||
<DismissibleUpsell upsellId="test-upsell" onClick={onClick}>
|
||||
|
|
@ -372,6 +387,11 @@ describe("DismissibleUpsell", () => {
|
|||
fireEvent.click(container)
|
||||
|
||||
expect(onClick).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Check that telemetry was tracked
|
||||
expect(mockCapture).toHaveBeenCalledWith(TelemetryEventName.UPSELL_CLICKED, {
|
||||
upsellId: "test-upsell",
|
||||
})
|
||||
})
|
||||
|
||||
it("does not call onClick when dismiss button is clicked", async () => {
|
||||
|
|
@ -470,7 +490,7 @@ describe("DismissibleUpsell", () => {
|
|||
})
|
||||
})
|
||||
|
||||
it("dismisses when clicked if dismissOnClick is true", async () => {
|
||||
it("dismisses when clicked if dismissOnClick is true and tracks both telemetry events", async () => {
|
||||
const onClick = vi.fn()
|
||||
const onDismiss = vi.fn()
|
||||
const { container } = render(
|
||||
|
|
@ -493,6 +513,14 @@ describe("DismissibleUpsell", () => {
|
|||
expect(onClick).toHaveBeenCalledTimes(1)
|
||||
expect(onDismiss).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Check that both telemetry events were tracked
|
||||
expect(mockCapture).toHaveBeenCalledWith(TelemetryEventName.UPSELL_CLICKED, {
|
||||
upsellId: "test-upsell",
|
||||
})
|
||||
expect(mockCapture).toHaveBeenCalledWith(TelemetryEventName.UPSELL_DISMISSED, {
|
||||
upsellId: "test-upsell",
|
||||
})
|
||||
|
||||
expect(mockPostMessage).toHaveBeenCalledWith({
|
||||
type: "dismissUpsell",
|
||||
upsellId: "test-upsell",
|
||||
|
|
@ -503,6 +531,46 @@ describe("DismissibleUpsell", () => {
|
|||
})
|
||||
})
|
||||
|
||||
it("dismisses on container click when dismissOnClick is true and no onClick is provided; tracks only dismissal", async () => {
|
||||
const onDismiss = vi.fn()
|
||||
const { container } = render(
|
||||
<DismissibleUpsell upsellId="test-upsell" onDismiss={onDismiss} dismissOnClick={true}>
|
||||
<div>Test content</div>
|
||||
</DismissibleUpsell>,
|
||||
)
|
||||
|
||||
// Make component visible
|
||||
makeUpsellVisible()
|
||||
|
||||
// Wait for component to be visible
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Test content")).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Click on the container (not the dismiss button)
|
||||
const containerDiv = screen.getByText("Test content").parentElement as HTMLElement
|
||||
fireEvent.click(containerDiv)
|
||||
|
||||
// onDismiss should be called
|
||||
expect(onDismiss).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Telemetry: only dismissal should be tracked
|
||||
expect(mockCapture).toHaveBeenCalledWith(TelemetryEventName.UPSELL_DISMISSED, {
|
||||
upsellId: "test-upsell",
|
||||
})
|
||||
expect(mockCapture).not.toHaveBeenCalledWith(TelemetryEventName.UPSELL_CLICKED, expect.anything())
|
||||
|
||||
// Dismiss message should be sent
|
||||
expect(mockPostMessage).toHaveBeenCalledWith({
|
||||
type: "dismissUpsell",
|
||||
upsellId: "test-upsell",
|
||||
})
|
||||
|
||||
// Component should be hidden
|
||||
await waitFor(() => {
|
||||
expect(container.firstChild).toBeNull()
|
||||
})
|
||||
})
|
||||
it("does not dismiss when clicked if dismissOnClick is false", async () => {
|
||||
const onClick = vi.fn()
|
||||
const onDismiss = vi.fn()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue