Compare commits

...

3 Commits

Author SHA1 Message Date
ShihChi Huang
fd6c9a56bd test: drop obsolete isCloud mock now that call sites no longer read it
The guard removal deleted the isCloud import from these composables, so the
distribution/types mock is dead — the cloud/OSS split now flows entirely
through the useTelemetry mock returning a dispatcher or null. Remove the
scaffolding; both characterization paths still pass via the null contract.
2026-06-23 18:16:21 -07:00
ShihChi Huang
a2bc5cd8f0 refactor: drop redundant isCloud guards around telemetry calls
useTelemetry() returns null in OSS builds, so useTelemetry()?.x() is already
a no-op there — the wrapping if (isCloud) guards duplicated that null check.
Remove them (dedent only, implementations unchanged) and drop the now-unused
isCloud imports. Genuine isCloud branches (cloud-only UI, JWT mint) untouched.
2026-06-23 18:16:21 -07:00
ShihChi Huang
49a4c81ad8 test: characterize cloud and OSS telemetry for template load and contact support
Pin both paths before removing the isCloud guards: trackTemplate and
trackHelpResourceClicked fire under isCloud=true and stay silent in OSS
(useTelemetry() returns null). The mock mirrors that real contract, so both
assertions hold against the guarded and unguarded code alike.
2026-06-23 18:16:20 -07:00
11 changed files with 129 additions and 89 deletions

View File

@@ -427,7 +427,6 @@ import { useIntersectionObserver } from '@/composables/useIntersectionObserver'
import { useLazyPagination } from '@/composables/useLazyPagination'
import { usePrimeVueOverlayChildStyle } from '@/composables/usePopoverSizing'
import { useTemplateFiltering } from '@/composables/useTemplateFiltering'
import { isCloud } from '@/platform/distribution/types'
import { useTelemetry } from '@/platform/telemetry'
import { useTemplateWorkflows } from '@/platform/workflow/templates/composables/useTemplateWorkflows'
import type { TemplateInfo } from '@/platform/workflow/templates/types/template'
@@ -453,16 +452,14 @@ onMounted(() => {
// Wrap onClose to track session end
const onClose = () => {
if (isCloud) {
const timeSpentSeconds = Math.floor(
(Date.now() - sessionStartTime.value) / 1000
)
const timeSpentSeconds = Math.floor(
(Date.now() - sessionStartTime.value) / 1000
)
useTelemetry()?.trackTemplateLibraryClosed({
template_selected: templateWasSelected.value,
time_spent_seconds: timeSpentSeconds
})
}
useTelemetry()?.trackTemplateLibraryClosed({
template_selected: templateWasSelected.value,
time_spent_seconds: timeSpentSeconds
})
originalOnClose()
}

View File

@@ -135,7 +135,6 @@ import Button from '@/components/ui/button/Button.vue'
import { useAuthActions } from '@/composables/auth/useAuthActions'
import { useFreeTierOnboarding } from '@/platform/cloud/onboarding/composables/useFreeTierOnboarding'
import { usePostAuthRedirect } from '@/platform/cloud/onboarding/composables/usePostAuthRedirect'
import { isCloud } from '@/platform/distribution/types'
import { useTelemetry } from '@/platform/telemetry'
import type { SignUpData } from '@/schemas/signInSchema'
import { isInChina } from '@/utils/networkUtil'
@@ -188,9 +187,7 @@ const signUpWithEmail = async (values: SignUpData) => {
}
onMounted(async () => {
if (isCloud) {
telemetry?.trackSignupOpened()
}
telemetry?.trackSignupOpened()
userIsInChina.value = await isInChina()
})

View File

@@ -18,7 +18,6 @@ import {
getSurveyCompletedStatus,
submitSurvey
} from '@/platform/cloud/onboarding/auth'
import { isCloud } from '@/platform/distribution/types'
import { remoteConfig } from '@/platform/remoteConfig/remoteConfig'
import { useTelemetry } from '@/platform/telemetry'
@@ -46,9 +45,7 @@ onMounted(async () => {
await router.replace({ name: 'cloud-user-check' })
return
}
if (isCloud) {
useTelemetry()?.trackSurvey('opened')
}
useTelemetry()?.trackSurvey('opened')
} catch (error) {
console.error('Failed to check survey status:', error)
}
@@ -62,9 +59,7 @@ const onSubmitSurvey = async (payload: Record<string, unknown>) => {
isSubmitting.value = true
try {
await submitSurvey(payload)
if (isCloud) {
useTelemetry()?.trackSurvey('submitted', payload)
}
useTelemetry()?.trackSurvey('submitted', payload)
await router.push({ name: 'cloud-user-check' })
} finally {
isSubmitting.value = false

View File

@@ -53,11 +53,9 @@ watch(
)
const handleSubscribe = () => {
if (isCloud) {
useTelemetry()?.trackSubscription('subscribe_clicked', {
current_tier: subscriptionTier.value?.toLowerCase()
})
}
useTelemetry()?.trackSubscription('subscribe_clicked', {
current_tier: subscriptionTier.value?.toLowerCase()
})
isAwaitingStripeSubscription.value = true
showSubscriptionDialog()
}

View File

@@ -237,12 +237,10 @@ function useSubscriptionInternal() {
const showSubscriptionDialog = (options?: {
reason?: SubscriptionDialogReason
}) => {
if (isCloud) {
useTelemetry()?.trackSubscription('modal_opened', {
current_tier: subscriptionTier.value?.toLowerCase(),
reason: options?.reason
})
}
useTelemetry()?.trackSubscription('modal_opened', {
current_tier: subscriptionTier.value?.toLowerCase(),
reason: options?.reason
})
void showSubscriptionRequiredDialog(options)
}

View File

@@ -38,6 +38,19 @@ vi.mock('@/stores/commandStore', () => ({
})
}))
// useTelemetry() returns null in OSS, a dispatcher in cloud — toggle via mockIsCloud.
const { mockIsCloud, mockTrackHelpResourceClicked } = vi.hoisted(() => ({
mockIsCloud: { value: true },
mockTrackHelpResourceClicked: vi.fn()
}))
vi.mock('@/platform/telemetry', () => ({
useTelemetry: () =>
mockIsCloud.value
? { trackHelpResourceClicked: mockTrackHelpResourceClicked }
: null
}))
// Mock window.open
const mockOpen = vi.fn()
Object.defineProperty(window, 'open', {
@@ -48,6 +61,7 @@ Object.defineProperty(window, 'open', {
describe('useSubscriptionActions', () => {
beforeEach(() => {
vi.clearAllMocks()
mockIsCloud.value = true
})
describe('handleAddApiCredits', () => {
@@ -73,6 +87,27 @@ describe('useSubscriptionActions', () => {
expect(isLoadingSupport.value).toBe(false)
})
it('tracks help-resource telemetry when messaging support in cloud', async () => {
const { handleMessageSupport } = useSubscriptionActions()
await handleMessageSupport()
expect(mockTrackHelpResourceClicked).toHaveBeenCalledWith({
resource_type: 'help_feedback',
is_external: true,
source: 'subscription'
})
})
it('does not fire telemetry when messaging support in OSS builds', async () => {
mockIsCloud.value = false
const { handleMessageSupport } = useSubscriptionActions()
await handleMessageSupport()
expect(mockTrackHelpResourceClicked).not.toHaveBeenCalled()
})
it('should handle errors gracefully', async () => {
mockExecute.mockRejectedValueOnce(new Error('Command failed'))
const { handleMessageSupport, isLoadingSupport } =

View File

@@ -2,7 +2,6 @@ import { onMounted, ref } from 'vue'
import { useBillingContext } from '@/composables/billing/useBillingContext'
import { useAuthActions } from '@/composables/auth/useAuthActions'
import { isCloud } from '@/platform/distribution/types'
import { useTelemetry } from '@/platform/telemetry'
import { useDialogService } from '@/services/dialogService'
import { useCommandStore } from '@/stores/commandStore'
@@ -30,13 +29,11 @@ export function useSubscriptionActions() {
const handleMessageSupport = async () => {
try {
isLoadingSupport.value = true
if (isCloud) {
telemetry?.trackHelpResourceClicked({
resource_type: 'help_feedback',
is_external: true,
source: 'subscription'
})
}
telemetry?.trackHelpResourceClicked({
resource_type: 'help_feedback',
is_external: true,
source: 'subscription'
})
await commandStore.execute('Comfy.ContactSupport')
} catch (error) {
console.error('[useSubscriptionActions] Error contacting support:', error)

View File

@@ -49,6 +49,17 @@ vi.mock('@/stores/dialogStore', () => ({
}))
}))
// useTelemetry() returns null in OSS, a dispatcher in cloud — toggle via mockIsCloud.
const { mockIsCloud, mockTrackTemplate } = vi.hoisted(() => ({
mockIsCloud: { value: true },
mockTrackTemplate: vi.fn()
}))
vi.mock('@/platform/telemetry', () => ({
useTelemetry: () =>
mockIsCloud.value ? { trackTemplate: mockTrackTemplate } : null
}))
// Mock fetch
global.fetch = vi.fn()
@@ -58,6 +69,9 @@ describe('useTemplateWorkflows', () => {
let mockWorkflowTemplatesStore: MockWorkflowTemplatesStore
beforeEach(() => {
mockIsCloud.value = true
mockTrackTemplate.mockClear()
mockWorkflowTemplatesStore = {
isLoaded: false,
loadWorkflowTemplates: vi.fn().mockResolvedValue(true),
@@ -285,6 +299,30 @@ describe('useTemplateWorkflows', () => {
expect(fetch).toHaveBeenCalledWith('mock-file-url/templates/template1.json')
})
it('tracks template telemetry on load in cloud builds', async () => {
const { loadWorkflowTemplate } = useTemplateWorkflows()
mockWorkflowTemplatesStore.isLoaded = true
await loadWorkflowTemplate('template1', 'default')
await flushPromises()
expect(mockTrackTemplate).toHaveBeenCalledWith({
workflow_name: 'template1',
template_source: 'default'
})
})
it('does not fire template telemetry in OSS builds', async () => {
mockIsCloud.value = false
const { loadWorkflowTemplate } = useTemplateWorkflows()
mockWorkflowTemplatesStore.isLoaded = true
await loadWorkflowTemplate('template1', 'default')
await flushPromises()
expect(mockTrackTemplate).not.toHaveBeenCalled()
})
it('should handle errors when loading templates', async () => {
const { loadWorkflowTemplate, loadingTemplateId } = useTemplateWorkflows()

View File

@@ -1,7 +1,6 @@
import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { isCloud } from '@/platform/distribution/types'
import { useTelemetry } from '@/platform/telemetry'
import { useWorkflowTemplatesStore } from '@/platform/workflow/templates/repositories/workflowTemplatesStore'
import type {
@@ -132,12 +131,10 @@ export function useTemplateWorkflows() {
? t(`templateWorkflows.template.${id}`, id)
: id
if (isCloud) {
useTelemetry()?.trackTemplate({
workflow_name: id,
template_source: sourceModule
})
}
useTelemetry()?.trackTemplate({
workflow_name: id,
template_source: sourceModule
})
dialogStore.closeDialog()
await app.loadGraphData(json, true, true, workflowName, {

View File

@@ -43,8 +43,6 @@ function getBasePath(): string {
const basePath = getBasePath()
function trackPageView(): void {
if (!isCloud || typeof window === 'undefined') return
useTelemetry()?.trackPageView(document.title, {
path: window.location.href
})

View File

@@ -361,15 +361,13 @@ export const useAuthStore = defineStore('auth', () => {
{ createCustomer: true }
)
if (isCloud) {
useTelemetry()?.trackAuth({
method: 'email',
is_new_user: false,
user_id: result.user.uid,
email: result.user.email ?? undefined,
...getShareAuthMetadata()
})
}
useTelemetry()?.trackAuth({
method: 'email',
is_new_user: false,
user_id: result.user.uid,
email: result.user.email ?? undefined,
...getShareAuthMetadata()
})
return result
}
@@ -384,15 +382,13 @@ export const useAuthStore = defineStore('auth', () => {
{ createCustomer: true }
)
if (isCloud) {
useTelemetry()?.trackAuth({
method: 'email',
is_new_user: true,
user_id: result.user.uid,
email: result.user.email ?? undefined,
...getShareAuthMetadata()
})
}
useTelemetry()?.trackAuth({
method: 'email',
is_new_user: true,
user_id: result.user.uid,
email: result.user.email ?? undefined,
...getShareAuthMetadata()
})
return result
}
@@ -405,17 +401,14 @@ export const useAuthStore = defineStore('auth', () => {
{ createCustomer: true }
)
if (isCloud) {
const additionalUserInfo = getAdditionalUserInfo(result)
useTelemetry()?.trackAuth({
method: 'google',
is_new_user:
options?.isNewUser || additionalUserInfo?.isNewUser || false,
user_id: result.user.uid,
email: result.user.email ?? undefined,
...getShareAuthMetadata()
})
}
const additionalUserInfo = getAdditionalUserInfo(result)
useTelemetry()?.trackAuth({
method: 'google',
is_new_user: options?.isNewUser || additionalUserInfo?.isNewUser || false,
user_id: result.user.uid,
email: result.user.email ?? undefined,
...getShareAuthMetadata()
})
return result
}
@@ -428,17 +421,14 @@ export const useAuthStore = defineStore('auth', () => {
{ createCustomer: true }
)
if (isCloud) {
const additionalUserInfo = getAdditionalUserInfo(result)
useTelemetry()?.trackAuth({
method: 'github',
is_new_user:
options?.isNewUser || additionalUserInfo?.isNewUser || false,
user_id: result.user.uid,
email: result.user.email ?? undefined,
...getShareAuthMetadata()
})
}
const additionalUserInfo = getAdditionalUserInfo(result)
useTelemetry()?.trackAuth({
method: 'github',
is_new_user: options?.isNewUser || additionalUserInfo?.isNewUser || false,
user_id: result.user.uid,
email: result.user.email ?? undefined,
...getShareAuthMetadata()
})
return result
}