Files
ComfyUI_frontend/tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
Luke Mino-Altherr 1522622427 fix: remove incorrect tooltip on remaining credit balance (#7383)
## Summary
Removed incorrect tooltip displayed on the remaining credit balance in
the subscription panel.

## Changes
- **What**: Removed unused `refreshTooltip` destructure and i18n
translation key
- **Breaking**: None

Fixes #6694

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7383-fix-remove-incorrect-tooltip-on-remaining-credit-balance-2c66d73d3650814eaee0f3c9006b7bd6)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-11 10:52:10 -08:00

316 lines
10 KiB
TypeScript

import { createTestingPinia } from '@pinia/testing'
import { mount } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { computed, ref } from 'vue'
import { createI18n } from 'vue-i18n'
import SubscriptionPanel from '@/platform/cloud/subscription/components/SubscriptionPanel.vue'
// Mock state refs that can be modified between tests
const mockIsActiveSubscription = ref(false)
const mockIsCancelled = ref(false)
const mockSubscriptionTier = ref<
'STANDARD' | 'CREATOR' | 'PRO' | 'FOUNDERS_EDITION' | null
>('CREATOR')
const TIER_TO_NAME: Record<string, string> = {
STANDARD: 'Standard',
CREATOR: 'Creator',
PRO: 'Pro',
FOUNDERS_EDITION: "Founder's Edition"
}
// Mock composables - using computed to match composable return types
const mockSubscriptionData = {
isActiveSubscription: computed(() => mockIsActiveSubscription.value),
isCancelled: computed(() => mockIsCancelled.value),
formattedRenewalDate: computed(() => '2024-12-31'),
formattedEndDate: computed(() => '2024-12-31'),
subscriptionTier: computed(() => mockSubscriptionTier.value),
subscriptionTierName: computed(() =>
mockSubscriptionTier.value ? TIER_TO_NAME[mockSubscriptionTier.value] : ''
),
handleInvoiceHistory: vi.fn()
}
const mockCreditsData = {
totalCredits: '10.00 Credits',
monthlyBonusCredits: '5.00 Credits',
prepaidCredits: '5.00 Credits',
isLoadingBalance: false
}
const mockActionsData = {
isLoadingSupport: false,
handleAddApiCredits: vi.fn(),
handleMessageSupport: vi.fn(),
handleRefresh: vi.fn(),
handleLearnMoreClick: vi.fn()
}
vi.mock('@/platform/cloud/subscription/composables/useSubscription', () => ({
useSubscription: () => mockSubscriptionData
}))
vi.mock(
'@/platform/cloud/subscription/composables/useSubscriptionCredits',
() => ({
useSubscriptionCredits: () => mockCreditsData
})
)
vi.mock(
'@/platform/cloud/subscription/composables/useSubscriptionActions',
() => ({
useSubscriptionActions: () => mockActionsData
})
)
vi.mock(
'@/platform/cloud/subscription/composables/useSubscriptionDialog',
() => ({
useSubscriptionDialog: () => ({
show: vi.fn()
})
})
)
// Create i18n instance for testing
const i18n = createI18n({
legacy: false,
locale: 'en',
messages: {
en: {
subscription: {
title: 'Subscription',
titleUnsubscribed: 'Subscribe',
perMonth: '/ month',
subscribeNow: 'Subscribe Now',
manageSubscription: 'Manage Subscription',
partnerNodesBalance: 'Partner Nodes Balance',
partnerNodesDescription: 'Credits for partner nodes',
totalCredits: 'Total Credits',
creditsRemainingThisMonth: 'Credits remaining this month',
creditsYouveAdded: "Credits you've added",
monthlyBonusDescription: 'Monthly bonus',
prepaidDescription: 'Prepaid credits',
monthlyCreditsRollover: 'Monthly credits rollover info',
prepaidCreditsInfo: 'Prepaid credits info',
viewUsageHistory: 'View Usage History',
addCredits: 'Add Credits',
yourPlanIncludes: 'Your plan includes',
viewMoreDetailsPlans: 'View more details about plans & pricing',
learnMore: 'Learn More',
messageSupport: 'Message Support',
invoiceHistory: 'Invoice History',
partnerNodesCredits: 'Partner nodes pricing',
renewsDate: 'Renews {date}',
expiresDate: 'Expires {date}',
tiers: {
founder: {
name: "Founder's Edition",
price: '20.00',
benefits: {
monthlyCredits: '5,460',
monthlyCreditsLabel: 'monthly credits',
maxDuration: '30 min',
maxDurationLabel: 'max duration of each workflow run',
gpuLabel: 'RTX 6000 Pro (96GB VRAM)',
addCreditsLabel: 'Add more credits whenever',
customLoRAsLabel: 'Import your own LoRAs'
}
},
standard: {
name: 'Standard',
price: '20.00',
benefits: {
monthlyCredits: '4,200',
monthlyCreditsLabel: 'monthly credits',
maxDuration: '30 min',
maxDurationLabel: 'max duration of each workflow run',
gpuLabel: 'RTX 6000 Pro (96GB VRAM)',
addCreditsLabel: 'Add more credits whenever',
customLoRAsLabel: 'Import your own LoRAs'
}
},
creator: {
name: 'Creator',
price: '35.00',
benefits: {
monthlyCredits: '7,400',
monthlyCreditsLabel: 'monthly credits',
maxDuration: '30 min',
maxDurationLabel: 'max duration of each workflow run',
gpuLabel: 'RTX 6000 Pro (96GB VRAM)',
addCreditsLabel: 'Add more credits whenever',
customLoRAsLabel: 'Import your own LoRAs'
}
},
pro: {
name: 'Pro',
price: '100.00',
benefits: {
monthlyCredits: '21,100',
monthlyCreditsLabel: 'monthly credits',
maxDuration: '1 hr',
maxDurationLabel: 'max duration of each workflow run',
gpuLabel: 'RTX 6000 Pro (96GB VRAM)',
addCreditsLabel: 'Add more credits whenever',
customLoRAsLabel: 'Import your own LoRAs'
}
}
}
}
}
}
})
function createWrapper(overrides = {}) {
return mount(SubscriptionPanel, {
global: {
plugins: [createTestingPinia({ createSpy: vi.fn }), i18n],
stubs: {
CloudBadge: true,
SubscribeButton: true,
SubscriptionBenefits: true,
Button: {
template:
'<button @click="$emit(\'click\')" :disabled="loading" :data-testid="label" :data-icon="icon">{{ label }}</button>',
props: [
'loading',
'label',
'icon',
'text',
'severity',
'size',
'iconPos',
'pt'
],
emits: ['click']
},
Skeleton: {
template: '<div class="skeleton"></div>'
}
}
},
...overrides
})
}
describe('SubscriptionPanel', () => {
beforeEach(() => {
vi.clearAllMocks()
// Reset mock state
mockIsActiveSubscription.value = false
mockIsCancelled.value = false
mockSubscriptionTier.value = 'CREATOR'
})
describe('subscription state functionality', () => {
it('shows correct UI for active subscription', () => {
mockIsActiveSubscription.value = true
const wrapper = createWrapper()
expect(wrapper.text()).toContain('Manage Subscription')
expect(wrapper.text()).toContain('Add Credits')
})
it('shows correct UI for inactive subscription', () => {
mockIsActiveSubscription.value = false
const wrapper = createWrapper()
expect(wrapper.findComponent({ name: 'SubscribeButton' }).exists()).toBe(
true
)
expect(wrapper.text()).not.toContain('Manage Subscription')
expect(wrapper.text()).not.toContain('Add Credits')
})
it('shows renewal date for active non-cancelled subscription', () => {
mockIsActiveSubscription.value = true
mockIsCancelled.value = false
const wrapper = createWrapper()
expect(wrapper.text()).toContain('Renews 2024-12-31')
})
it('shows expiry date for cancelled subscription', () => {
mockIsActiveSubscription.value = true
mockIsCancelled.value = true
const wrapper = createWrapper()
expect(wrapper.text()).toContain('Expires 2024-12-31')
})
it('displays FOUNDERS_EDITION tier correctly', () => {
mockSubscriptionTier.value = 'FOUNDERS_EDITION'
const wrapper = createWrapper()
expect(wrapper.text()).toContain("Founder's Edition")
expect(wrapper.text()).toContain('5,460')
})
it('displays CREATOR tier correctly', () => {
mockSubscriptionTier.value = 'CREATOR'
const wrapper = createWrapper()
expect(wrapper.text()).toContain('Creator')
expect(wrapper.text()).toContain('7,400')
})
})
describe('credit display functionality', () => {
it('displays dynamic credit values correctly', () => {
const wrapper = createWrapper()
expect(wrapper.text()).toContain('10.00 Credits')
expect(wrapper.text()).toContain('5.00 Credits')
})
it('shows loading skeleton when fetching balance', () => {
mockCreditsData.isLoadingBalance = true
const wrapper = createWrapper()
expect(wrapper.findAll('.skeleton').length).toBeGreaterThan(0)
})
it('hides skeleton when balance loaded', () => {
mockCreditsData.isLoadingBalance = false
const wrapper = createWrapper()
expect(wrapper.findAll('.skeleton').length).toBe(0)
})
})
describe('action buttons', () => {
it('should call handleLearnMoreClick when learn more is clicked', async () => {
const wrapper = createWrapper()
const learnMoreButton = wrapper.find('[data-testid="Learn More"]')
await learnMoreButton.trigger('click')
expect(mockActionsData.handleLearnMoreClick).toHaveBeenCalledOnce()
})
it('should call handleMessageSupport when message support is clicked', async () => {
const wrapper = createWrapper()
const supportButton = wrapper.find('[data-testid="Message Support"]')
await supportButton.trigger('click')
expect(mockActionsData.handleMessageSupport).toHaveBeenCalledOnce()
})
it('should call handleRefresh when refresh button is clicked', async () => {
const wrapper = createWrapper()
// Find the refresh button by icon
const refreshButton = wrapper.find('[data-icon="pi pi-sync"]')
await refreshButton.trigger('click')
expect(mockActionsData.handleRefresh).toHaveBeenCalledOnce()
})
})
describe('loading states', () => {
it('should show loading state on support button when loading', () => {
mockActionsData.isLoadingSupport = true
const wrapper = createWrapper()
const supportButton = wrapper.find('[data-testid="Message Support"]')
expect(supportButton.attributes('disabled')).toBeDefined()
})
it('should show loading state on refresh button when loading balance', () => {
mockCreditsData.isLoadingBalance = true
const wrapper = createWrapper()
const refreshButton = wrapper.find('[data-icon="pi pi-sync"]')
expect(refreshButton.attributes('disabled')).toBeDefined()
})
})
})