chore: migrate tests from tests-ui/ to colocate with source files (#7811)

## Summary

Migrates all unit tests from `tests-ui/` to colocate with their source
files in `src/`, improving discoverability and maintainability.

## Changes

- **What**: Relocated all unit tests to be adjacent to the code they
test, following the `<source>.test.ts` naming convention
- **Config**: Updated `vitest.config.ts` to remove `tests-ui` include
pattern and `@tests-ui` alias
- **Docs**: Moved testing documentation to `docs/testing/` with updated
paths and patterns

## Review Focus

- Migration patterns documented in
`temp/plans/migrate-tests-ui-to-src.md`
- Tests use `@/` path aliases instead of relative imports
- Shared fixtures placed in `__fixtures__/` directories

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7811-chore-migrate-tests-from-tests-ui-to-colocate-with-source-files-2da6d73d36508147a4cce85365dee614)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Alexander Brown
2026-01-05 16:32:24 -08:00
committed by GitHub
parent 832588c7a9
commit 10feb1fd5b
272 changed files with 483 additions and 1239 deletions

View File

@@ -0,0 +1,441 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { ref } from 'vue'
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
// Create mocks
const mockIsLoggedIn = ref(false)
const mockReportError = vi.fn()
const mockAccessBillingPortal = vi.fn()
const mockShowSubscriptionRequiredDialog = vi.fn()
const mockGetAuthHeader = vi.fn(() =>
Promise.resolve({ Authorization: 'Bearer test-token' })
)
const mockTelemetry = {
trackSubscription: vi.fn(),
trackMonthlySubscriptionCancelled: vi.fn()
}
// Mock dependencies
vi.mock('@/composables/auth/useCurrentUser', () => ({
useCurrentUser: vi.fn(() => ({
isLoggedIn: mockIsLoggedIn
}))
}))
vi.mock('@/platform/telemetry', () => ({
useTelemetry: vi.fn(() => mockTelemetry)
}))
vi.mock('@/composables/auth/useFirebaseAuthActions', () => ({
useFirebaseAuthActions: vi.fn(() => ({
reportError: mockReportError,
accessBillingPortal: mockAccessBillingPortal
}))
}))
vi.mock('@/composables/useErrorHandling', () => ({
useErrorHandling: vi.fn(() => ({
wrapWithErrorHandlingAsync: vi.fn(
(fn, errorHandler) =>
async (...args: any[]) => {
try {
return await fn(...args)
} catch (error) {
if (errorHandler) {
errorHandler(error)
}
throw error
}
}
)
}))
}))
vi.mock('@/platform/distribution/types', () => ({
isCloud: true
}))
vi.mock('@/services/dialogService', () => ({
useDialogService: vi.fn(() => ({
showSubscriptionRequiredDialog: mockShowSubscriptionRequiredDialog
}))
}))
vi.mock('@/stores/firebaseAuthStore', () => ({
useFirebaseAuthStore: vi.fn(() => ({
getAuthHeader: mockGetAuthHeader
})),
FirebaseAuthStoreError: class extends Error {}
}))
// Mock fetch
global.fetch = vi.fn()
describe('useSubscription', () => {
beforeEach(() => {
vi.clearAllMocks()
mockIsLoggedIn.value = false
mockTelemetry.trackSubscription.mockReset()
mockTelemetry.trackMonthlySubscriptionCancelled.mockReset()
window.__CONFIG__ = {
subscription_required: true
} as typeof window.__CONFIG__
vi.mocked(global.fetch).mockResolvedValue({
ok: true,
json: async () => ({
is_active: false,
subscription_id: '',
renewal_date: ''
})
} as Response)
})
describe('computed properties', () => {
it('should compute isActiveSubscription correctly when subscription is active', async () => {
vi.mocked(global.fetch).mockResolvedValue({
ok: true,
json: async () => ({
is_active: true,
subscription_id: 'sub_123',
renewal_date: '2025-11-16'
})
} as Response)
mockIsLoggedIn.value = true
const { isActiveSubscription, fetchStatus } = useSubscription()
await fetchStatus()
expect(isActiveSubscription.value).toBe(true)
})
it('should compute isActiveSubscription as false when subscription is inactive', async () => {
vi.mocked(global.fetch).mockResolvedValue({
ok: true,
json: async () => ({
is_active: false,
subscription_id: 'sub_123',
renewal_date: '2025-11-16'
})
} as Response)
mockIsLoggedIn.value = true
const { isActiveSubscription, fetchStatus } = useSubscription()
await fetchStatus()
expect(isActiveSubscription.value).toBe(false)
})
it('should format renewal date correctly', async () => {
vi.mocked(global.fetch).mockResolvedValue({
ok: true,
json: async () => ({
is_active: true,
subscription_id: 'sub_123',
renewal_date: '2025-11-16T12:00:00Z'
})
} as Response)
mockIsLoggedIn.value = true
const { formattedRenewalDate, fetchStatus } = useSubscription()
await fetchStatus()
// The date format may vary based on timezone, so we just check it's a valid date string
expect(formattedRenewalDate.value).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/)
expect(formattedRenewalDate.value).toContain('2025')
expect(formattedRenewalDate.value).toContain('Nov')
})
it('should return empty string when renewal date is not available', () => {
const { formattedRenewalDate } = useSubscription()
expect(formattedRenewalDate.value).toBe('')
})
it('should return subscription tier from status', async () => {
vi.mocked(global.fetch).mockResolvedValue({
ok: true,
json: async () => ({
is_active: true,
subscription_id: 'sub_123',
subscription_tier: 'CREATOR',
renewal_date: '2025-11-16T12:00:00Z'
})
} as Response)
mockIsLoggedIn.value = true
const { subscriptionTier, fetchStatus } = useSubscription()
await fetchStatus()
expect(subscriptionTier.value).toBe('CREATOR')
})
it('should return null when subscription tier is not available', () => {
const { subscriptionTier } = useSubscription()
expect(subscriptionTier.value).toBeNull()
})
})
describe('fetchStatus', () => {
it('should fetch subscription status successfully', async () => {
const mockStatus = {
is_active: true,
subscription_id: 'sub_123',
renewal_date: '2025-11-16'
}
vi.mocked(global.fetch).mockResolvedValue({
ok: true,
json: async () => mockStatus
} as Response)
mockIsLoggedIn.value = true
const { fetchStatus } = useSubscription()
await fetchStatus()
expect(global.fetch).toHaveBeenCalledWith(
expect.stringContaining('/customers/cloud-subscription-status'),
expect.objectContaining({
headers: expect.objectContaining({
Authorization: 'Bearer test-token',
'Content-Type': 'application/json'
})
})
)
})
it('should handle fetch errors gracefully', async () => {
vi.mocked(global.fetch).mockResolvedValue({
ok: false,
json: async () => ({ message: 'Subscription not found' })
} as Response)
const { fetchStatus } = useSubscription()
await expect(fetchStatus()).rejects.toThrow()
})
})
describe('subscribe', () => {
it('should initiate subscription checkout successfully', async () => {
const checkoutUrl = 'https://checkout.stripe.com/test'
vi.mocked(global.fetch).mockResolvedValue({
ok: true,
json: async () => ({ checkout_url: checkoutUrl })
} as Response)
// Mock window.open
const windowOpenSpy = vi
.spyOn(window, 'open')
.mockImplementation(() => null)
const { subscribe } = useSubscription()
await subscribe()
expect(global.fetch).toHaveBeenCalledWith(
expect.stringContaining('/customers/cloud-subscription-checkout'),
expect.objectContaining({
method: 'POST',
headers: expect.objectContaining({
Authorization: 'Bearer test-token',
'Content-Type': 'application/json'
})
})
)
expect(windowOpenSpy).toHaveBeenCalledWith(checkoutUrl, '_blank')
windowOpenSpy.mockRestore()
})
it('should throw error when checkout URL is not returned', async () => {
vi.mocked(global.fetch).mockResolvedValue({
ok: true,
json: async () => ({})
} as Response)
const { subscribe } = useSubscription()
await expect(subscribe()).rejects.toThrow()
})
})
describe('requireActiveSubscription', () => {
it('should not show dialog when subscription is active', async () => {
vi.mocked(global.fetch).mockResolvedValue({
ok: true,
json: async () => ({
is_active: true,
subscription_id: 'sub_123',
renewal_date: '2025-11-16'
})
} as Response)
const { requireActiveSubscription } = useSubscription()
await requireActiveSubscription()
expect(mockShowSubscriptionRequiredDialog).not.toHaveBeenCalled()
})
it('should show dialog when subscription is inactive', async () => {
vi.mocked(global.fetch).mockResolvedValue({
ok: true,
json: async () => ({
is_active: false,
subscription_id: 'sub_123',
renewal_date: '2025-11-16'
})
} as Response)
const { requireActiveSubscription } = useSubscription()
await requireActiveSubscription()
expect(mockShowSubscriptionRequiredDialog).toHaveBeenCalled()
})
})
describe('action handlers', () => {
it('should open usage history URL', () => {
const windowOpenSpy = vi
.spyOn(window, 'open')
.mockImplementation(() => null)
const { handleViewUsageHistory } = useSubscription()
handleViewUsageHistory()
expect(windowOpenSpy).toHaveBeenCalledWith(
'https://stagingplatform.comfy.org/profile/usage',
'_blank'
)
windowOpenSpy.mockRestore()
})
it('should open learn more URL', () => {
const windowOpenSpy = vi
.spyOn(window, 'open')
.mockImplementation(() => null)
const { handleLearnMore } = useSubscription()
handleLearnMore()
expect(windowOpenSpy).toHaveBeenCalledWith(
'https://docs.comfy.org',
'_blank'
)
windowOpenSpy.mockRestore()
})
it('should call accessBillingPortal for invoice history', async () => {
const { handleInvoiceHistory } = useSubscription()
await handleInvoiceHistory()
expect(mockAccessBillingPortal).toHaveBeenCalled()
})
it('should call accessBillingPortal for manage subscription', async () => {
const { manageSubscription } = useSubscription()
await manageSubscription()
expect(mockAccessBillingPortal).toHaveBeenCalled()
})
it('tracks cancellation after manage subscription when status flips', async () => {
vi.useFakeTimers()
mockIsLoggedIn.value = true
const activeResponse = {
ok: true,
json: async () => ({
is_active: true,
subscription_id: 'sub_active',
renewal_date: '2025-11-16'
})
}
const cancelledResponse = {
ok: true,
json: async () => ({
is_active: false,
subscription_id: 'sub_cancelled',
renewal_date: '2025-11-16',
end_date: '2025-12-01'
})
}
vi.mocked(global.fetch)
.mockResolvedValueOnce(activeResponse as Response)
.mockResolvedValueOnce(activeResponse as Response)
.mockResolvedValueOnce(cancelledResponse as Response)
try {
const { fetchStatus, manageSubscription } = useSubscription()
await fetchStatus()
await manageSubscription()
await vi.advanceTimersByTimeAsync(5000)
expect(
mockTelemetry.trackMonthlySubscriptionCancelled
).toHaveBeenCalledTimes(1)
} finally {
vi.useRealTimers()
}
})
it('handles rapid focus events during cancellation polling', async () => {
vi.useFakeTimers()
mockIsLoggedIn.value = true
const activeResponse = {
ok: true,
json: async () => ({
is_active: true,
subscription_id: 'sub_active',
renewal_date: '2025-11-16'
})
}
const cancelledResponse = {
ok: true,
json: async () => ({
is_active: false,
subscription_id: 'sub_cancelled',
renewal_date: '2025-11-16',
end_date: '2025-12-01'
})
}
vi.mocked(global.fetch)
.mockResolvedValueOnce(activeResponse as Response)
.mockResolvedValueOnce(activeResponse as Response)
.mockResolvedValueOnce(cancelledResponse as Response)
try {
const { fetchStatus, manageSubscription } = useSubscription()
await fetchStatus()
await manageSubscription()
window.dispatchEvent(new Event('focus'))
await vi.waitFor(() => {
expect(
mockTelemetry.trackMonthlySubscriptionCancelled
).toHaveBeenCalledTimes(1)
})
} finally {
vi.useRealTimers()
}
})
})
})