Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/locales/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -2012,6 +2012,7 @@
"videoEstimateHelp": "What is this?",
"videoEstimateExplanation": "These estimates are based on the Wan Fun Control template for 5-second videos.",
"videoEstimateTryTemplate": "Try the Wan Fun Control template →",
"upgradePlan": "Upgrade Plan",
"upgradeTo": "Upgrade to {plan}",
"changeTo": "Change to {plan}",
"credits": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<div class="grow overflow-auto">
<div class="rounded-2xl border border-interface-stroke p-6">
<div>
<div class="flex items-center justify-between">
<div class="flex items-center justify-between gap-2">
<div class="flex flex-col gap-2">
<div class="text-sm font-bold text-text-primary">
{{ subscriptionTierName }}
Expand Down Expand Up @@ -49,11 +49,30 @@
</template>
</div>
</div>

<Button
v-if="isActiveSubscription"
:label="$t('subscription.manageSubscription')"
severity="secondary"
class="bg-interface-menu-component-surface-selected"
class="ml-auto bg-interface-menu-component-surface-selected"
:pt="{
root: {
style: 'border-radius: 8px; padding: 8px 16px;'
},
label: {
class: 'text-sm font-normal text-text-primary'
}
}"
@click="
async () => {
await authActions.accessBillingPortal()
}
"
/>
<Button
v-if="isActiveSubscription"
:label="$t('subscription.upgradePlan')"
severity="primary"
:pt="{
root: {
style: 'border-radius: 8px; padding: 8px 16px;'
Expand All @@ -64,6 +83,7 @@
}"
@click="showSubscriptionDialog"
/>

<SubscribeButton
v-else
:label="$t('subscription.subscribeNow')"
Expand Down Expand Up @@ -334,6 +354,7 @@ import { computed } from 'vue'
import { useI18n } from 'vue-i18n'

import CloudBadge from '@/components/topbar/CloudBadge.vue'
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
import { useExternalLink } from '@/composables/useExternalLink'
import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
Expand All @@ -358,6 +379,7 @@ type TierKey = (typeof TIER_TO_I18N_KEY)[SubscriptionTier]
const DEFAULT_TIER_KEY: TierKey = 'standard'

const { buildDocsUrl } = useExternalLink()
const authActions = useFirebaseAuthActions()
const { t } = useI18n()

const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ vi.mock(
})
)

vi.mock('@/composables/auth/useFirebaseAuthActions', () => ({
useFirebaseAuthActions: vi.fn(() => ({
authActions: vi.fn(() => ({
accessBillingPortal: vi.fn()
}))
}))
}))

Comment on lines +78 to +85
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the composable definition
fd -e ts -e js "useFirebaseAuthActions" src/ --exec cat -n {} +

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 8410


🏁 Script executed:

find src -name "*useFirebaseAuthActions*" -type f

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 116


🏁 Script executed:

rg "useFirebaseAuthActions" --type ts -l | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 712


🏁 Script executed:

find src -path "*composables/auth*" -type f | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 155


🏁 Script executed:

cat -n tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts | head -100

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 3639


🏁 Script executed:

rg "export.*function.*useFirebaseAuthActions|export.*const.*useFirebaseAuthActions" -A 15 --type ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1375


🏁 Script executed:

rg "useFirebaseAuthActions" -A 20 --type ts --max-count 5

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 27592


🏁 Script executed:

wc -l tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 153


🏁 Script executed:

cat -n tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 13161


🏁 Script executed:

cat -n src/platform/cloud/subscription/components/SubscriptionPanel.vue 2>/dev/null | head -150

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 6460


🏁 Script executed:

find . -path "*/composables/auth/*" -type f 2>/dev/null | grep -v node_modules

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 159


🏁 Script executed:

git ls-files "src/**/composables/**" | grep -i auth

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

git ls-files "src/**" | grep -i firebase

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 171


🏁 Script executed:

ls -la src/ 2>/dev/null | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1179


🏁 Script executed:

git ls-files | grep -E "(useFirebaseAuthActions|SubscriptionPanel)" | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 261


🏁 Script executed:

sed -n '60,70p' src/platform/cloud/subscription/components/SubscriptionPanel.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 446


🏁 Script executed:

grep -n "authActions\|useFirebaseAuthActions" src/platform/cloud/subscription/components/SubscriptionPanel.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 268


🏁 Script executed:

grep -A 50 "<script" src/platform/cloud/subscription/components/SubscriptionPanel.vue | head -60

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1969


🏁 Script executed:

wc -l src/platform/cloud/subscription/components/SubscriptionPanel.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 138


🏁 Script executed:

cat src/platform/cloud/subscription/components/SubscriptionPanel.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 16203


🏁 Script executed:

grep -n "authActions\|useFirebaseAuthActions" src/platform/cloud/subscription/composables/useSubscriptionActions.ts -A 3 | head -30

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 732


🏁 Script executed:

wc -l src/platform/cloud/subscription/composables/useSubscriptionActions.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 142


🏁 Script executed:

cat src/platform/cloud/subscription/composables/useSubscriptionActions.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1999


🏁 Script executed:

grep -B 10 -A 10 "authActions" src/platform/cloud/subscription/components/SubscriptionPanel.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1287


🏁 Script executed:

sed -n '200,300p' src/platform/cloud/subscription/components/SubscriptionPanel.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 3579


🏁 Script executed:

file src/platform/cloud/subscription/components/SubscriptionPanel.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 114


🏁 Script executed:

tail -100 src/platform/cloud/subscription/components/SubscriptionPanel.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2561


Fix mock shape to match real composable return type and add behavioral test for "Manage Subscription" click.

The mock incorrectly wraps accessBillingPortal in a nested authActions function. The real useFirebaseAuthActions() returns an object with methods directly ({ logout, sendPasswordReset, purchaseCredits, accessBillingPortal, ... }), not nested under a function.

+const accessBillingPortalMock = vi.fn()
+
 vi.mock('@/composables/auth/useFirebaseAuthActions', () => ({
-  useFirebaseAuthActions: vi.fn(() => ({
-    authActions: vi.fn(() => ({
-      accessBillingPortal: vi.fn()
-    }))
-  }))
+  useFirebaseAuthActions: () => ({
+    logout: vi.fn(),
+    sendPasswordReset: vi.fn(),
+    purchaseCredits: vi.fn(),
+    accessBillingPortal: accessBillingPortalMock,
+    fetchBalance: vi.fn(),
+    signInWithGoogle: vi.fn(),
+    signInWithGithub: vi.fn(),
+    signInWithEmail: vi.fn(),
+    signUpWithEmail: vi.fn(),
+    updatePassword: vi.fn(),
+    deleteAccount: vi.fn(),
+    accessError: ref(false),
+    reportError: vi.fn()
+  })
 }))

Add test for the "Manage Subscription" button click:

   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('calls accessBillingPortal when Manage Subscription button is clicked', async () => {
+    mockIsActiveSubscription.value = true
+    const wrapper = createWrapper()
+    const manageButton = wrapper.find('[data-testid="Manage Subscription"]')
+    expect(manageButton.exists()).toBe(true)
+    await manageButton.trigger('click')
+    expect(accessBillingPortalMock).toHaveBeenCalledOnce()
+  })
🤖 Prompt for AI Agents
In
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
around lines 78 to 85, the vi.mock currently nests accessBillingPortal under
authActions() but the real useFirebaseAuthActions() returns an object whose
methods are top-level; change the mock to return an object with
accessBillingPortal: vi.fn() (and any other used methods) directly instead of
authActions: vi.fn(...). Then add a behavioral test that renders
SubscriptionPanel, finds the "Manage Subscription" button (by role or text),
simulates a user click (userEvent.click) and asserts that the mocked
accessBillingPortal was called once. Ensure the mock is reset between tests
(vi.clearAllMocks or similar) so the call assertion is reliable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, keep the mock simple until we have a real reusable mock.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DrJKL, understood! We'll keep the mock simple as-is for now. 👍


🧠 Learnings used
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Mock external dependencies in tests

Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : Keep module mocks contained in test files; do not use global mutable state within test files; use `vi.hoisted()` if necessary

Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : Leverage Vitest's mocking utilities where possible for test mocking

Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Always prefer vitest mock functions over writing verbose manual mocks

Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/{composables,components}/**/*.{ts,tsx,vue} : Clean up subscriptions in state management to prevent memory leaks

Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : For Component testing, use Vue Test Utils and follow advice about making components easy to test

Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new features

Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible

Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : Do not write tests that just test the mocks; ensure tests fail when code behaves unexpectedly

Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : Follow 'Don't Mock What You Don't Own' principle - avoid mocking external dependencies

// Create i18n instance for testing
const i18n = createI18n({
legacy: false,
Expand Down