mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-05 05:32:02 +00:00
## Summary Rename `useFirebaseAuthStore` → `useAuthStore` and `FirebaseAuthStoreError` → `AuthStoreError`. Introduce shared mock factory (`authStoreMock.ts`) to replace 16 independent bespoke mocks. ## Changes - **What**: Mechanical rename of store, composable, class, and store ID (`firebaseAuth` → `auth`). Created `src/stores/__tests__/authStoreMock.ts` — a shared mock factory with reactive controls, used by all consuming test files. Migrated all 16 test files from ad-hoc mocks to the shared factory. - **Files**: 62 files changed (rename propagation + new test infra) ## Review Focus - Mock factory API design in `authStoreMock.ts` — covers all store properties with reactive `controls` for per-test customization - Self-test in `authStoreMock.test.ts` validates computed reactivity Fixes #8219 ## Stack This is PR 1/5 in a stacked refactoring series: 1. **→ This PR**: Rename + shared test fixtures 2. #10484: Extract auth-routing from workspaceApi 3. #10485: Auth token priority tests 4. #10486: Decompose MembersPanelContent 5. #10487: Consolidate SubscriptionTier type --------- Co-authored-by: Alexander Brown <drjkl@comfy.org>
123 lines
3.6 KiB
TypeScript
123 lines
3.6 KiB
TypeScript
import { storeToRefs } from 'pinia'
|
|
|
|
import { getComfyApiBaseUrl } from '@/config/comfyApi'
|
|
import { t } from '@/i18n'
|
|
import { isCloud } from '@/platform/distribution/types'
|
|
import { useTelemetry } from '@/platform/telemetry'
|
|
import { AuthStoreError, useAuthStore } from '@/stores/authStore'
|
|
import type { CheckoutAttributionMetadata } from '@/platform/telemetry/types'
|
|
import type { TierKey } from '@/platform/cloud/subscription/constants/tierPricing'
|
|
import type { BillingCycle } from './subscriptionTierRank'
|
|
|
|
type CheckoutTier = TierKey | `${TierKey}-yearly`
|
|
|
|
const getCheckoutTier = (
|
|
tierKey: TierKey,
|
|
billingCycle: BillingCycle
|
|
): CheckoutTier => (billingCycle === 'yearly' ? `${tierKey}-yearly` : tierKey)
|
|
|
|
const getCheckoutAttributionForCloud =
|
|
async (): Promise<CheckoutAttributionMetadata> => {
|
|
if (__DISTRIBUTION__ !== 'cloud') {
|
|
return {}
|
|
}
|
|
|
|
const { getCheckoutAttribution } =
|
|
await import('@/platform/telemetry/utils/checkoutAttribution')
|
|
|
|
return getCheckoutAttribution()
|
|
}
|
|
|
|
/**
|
|
* Core subscription checkout logic shared between PricingTable and
|
|
* SubscriptionRedirectView. Handles:
|
|
* - Ensuring the user is authenticated
|
|
* - Calling the backend checkout endpoint
|
|
* - Normalizing error responses
|
|
* - Opening the checkout URL in a new tab when available
|
|
*
|
|
* Callers are responsible for:
|
|
* - Guarding on cloud-only behavior (isCloud)
|
|
* - Managing loading state
|
|
* - Wrapping with error handling (e.g. useErrorHandling)
|
|
*/
|
|
export async function performSubscriptionCheckout(
|
|
tierKey: TierKey,
|
|
currentBillingCycle: BillingCycle,
|
|
openInNewTab: boolean = true
|
|
): Promise<void> {
|
|
if (!isCloud) return
|
|
|
|
const authStore = useAuthStore()
|
|
const { userId } = storeToRefs(authStore)
|
|
const telemetry = useTelemetry()
|
|
const authHeader = await authStore.getAuthHeader()
|
|
|
|
if (!authHeader) {
|
|
throw new AuthStoreError(t('toastMessages.userNotAuthenticated'))
|
|
}
|
|
|
|
const checkoutTier = getCheckoutTier(tierKey, currentBillingCycle)
|
|
let checkoutAttribution: CheckoutAttributionMetadata = {}
|
|
try {
|
|
checkoutAttribution = await getCheckoutAttributionForCloud()
|
|
} catch (error) {
|
|
console.warn(
|
|
'[SubscriptionCheckout] Failed to collect checkout attribution',
|
|
error
|
|
)
|
|
}
|
|
const checkoutPayload = { ...checkoutAttribution }
|
|
|
|
const response = await fetch(
|
|
`${getComfyApiBaseUrl()}/customers/cloud-subscription-checkout/${checkoutTier}`,
|
|
{
|
|
method: 'POST',
|
|
headers: { ...authHeader, 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(checkoutPayload)
|
|
}
|
|
)
|
|
|
|
if (!response.ok) {
|
|
let errorMessage = 'Failed to initiate checkout'
|
|
try {
|
|
const errorData = await response.json()
|
|
errorMessage = errorData.message || errorMessage
|
|
} catch {
|
|
// If JSON parsing fails, try to get text response or use HTTP status
|
|
try {
|
|
const errorText = await response.text()
|
|
errorMessage =
|
|
errorText || `HTTP ${response.status} ${response.statusText}`
|
|
} catch {
|
|
errorMessage = `HTTP ${response.status} ${response.statusText}`
|
|
}
|
|
}
|
|
|
|
throw new AuthStoreError(
|
|
t('toastMessages.failedToInitiateSubscription', {
|
|
error: errorMessage
|
|
})
|
|
)
|
|
}
|
|
|
|
const data = await response.json()
|
|
|
|
if (data.checkout_url) {
|
|
if (userId.value) {
|
|
telemetry?.trackBeginCheckout({
|
|
user_id: userId.value,
|
|
tier: tierKey,
|
|
cycle: currentBillingCycle,
|
|
checkout_type: 'new',
|
|
...checkoutAttribution
|
|
})
|
|
}
|
|
if (openInNewTab) {
|
|
window.open(data.checkout_url, '_blank')
|
|
} else {
|
|
globalThis.location.href = data.checkout_url
|
|
}
|
|
}
|
|
}
|