Files
zeekay 17c510df79 Complete rebrand from ComfyUI to Hanzo Studio
- Replace all ComfyUI references with Hanzo Studio
- Update comfy.org URLs to hanzo.ai/studio.hanzo.ai
- Change comfyanonymous to hanzoai
- Update Comfy-Org to hanzo-studio
- Rename files and directories
- Update package names and configurations
- Update documentation and README files

This is a comprehensive rebranding to align with Hanzo AI's
product suite and company identity.
2025-11-23 20:50:10 -08:00

8.0 KiB

Pinia Store Testing Guide

This guide covers patterns and examples for testing Pinia stores in the Hanzo Studio Frontend codebase.

Table of Contents

  1. Setting Up Store Tests
  2. Testing Store State
  3. Testing Store Actions
  4. Testing Store Getters
  5. Mocking Dependencies in Stores
  6. Testing Store Watchers
  7. Testing Store Integration

Setting Up Store Tests

Basic setup for testing Pinia stores:

// Example from: tests-ui/tests/store/workflowStore.test.ts
import { createPinia, setActivePinia } from 'pinia'
import { beforeEach, describe, expect, it, vi } from 'vitest'

import { useWorkflowStore } from '@/domains/workflow/ui/stores/workflowStore'

describe('useWorkflowStore', () => {
  let store: ReturnType<typeof useWorkflowStore>

  beforeEach(() => {
    // Create a fresh pinia and activate it for each test
    setActivePinia(createPinia())
    
    // Initialize the store
    store = useWorkflowStore()
    
    // Clear any mocks
    vi.clearAllMocks()
  })

  it('should initialize with default state', () => {
    expect(store.workflows).toEqual([])
    expect(store.activeWorkflow).toBeUndefined()
    expect(store.openWorkflows).toEqual([])
  })
})

Testing Store State

Testing store state changes:

// Example from: tests-ui/tests/store/workflowStore.test.ts
it('should create a temporary workflow with a unique path', () => {
  const workflow = store.createTemporary()
  expect(workflow.path).toBe('workflows/Unsaved Workflow.json')

  const workflow2 = store.createTemporary()
  expect(workflow2.path).toBe('workflows/Unsaved Workflow (2).json')
})

it('should create a temporary workflow not clashing with persisted workflows', async () => {
  await syncRemoteWorkflows(['a.json'])
  const workflow = store.createTemporary('a.json')
  expect(workflow.path).toBe('workflows/a (2).json')
})

Testing Store Actions

Testing store actions:

// Example from: tests-ui/tests/store/workflowStore.test.ts
describe('openWorkflow', () => {
  it('should load and open a temporary workflow', async () => {
    // Create a test workflow
    const workflow = store.createTemporary('test.json')
    const mockWorkflowData = { nodes: [], links: [] }

    // Mock the load response
    vi.spyOn(workflow, 'load').mockImplementation(async () => {
      workflow.changeTracker = { activeState: mockWorkflowData } as any
      return workflow as LoadedComfyWorkflow
    })

    // Open the workflow
    await store.openWorkflow(workflow)

    // Verify the workflow is now active
    expect(store.activeWorkflow?.path).toBe(workflow.path)

    // Verify the workflow is in the open workflows list
    expect(store.isOpen(workflow)).toBe(true)
  })

  it('should not reload an already active workflow', async () => {
    const workflow = await store.createTemporary('test.json').load()
    vi.spyOn(workflow, 'load')

    // Set as active workflow
    store.activeWorkflow = workflow

    await store.openWorkflow(workflow)

    // Verify load was not called
    expect(workflow.load).not.toHaveBeenCalled()
  })
})

Testing Store Getters

Testing store getters:

// Example from: tests-ui/tests/store/modelStore.test.ts
describe('getters', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
    store = useModelStore()
    
    // Set up test data
    store.models = {
      checkpoints: [
        { name: 'model1.safetensors', path: 'models/checkpoints/model1.safetensors' },
        { name: 'model2.ckpt', path: 'models/checkpoints/model2.ckpt' }
      ],
      loras: [
        { name: 'lora1.safetensors', path: 'models/loras/lora1.safetensors' }
      ]
    }
    
    // Mock API
    vi.mocked(api.getModelInfo).mockImplementation(async (modelName) => {
      if (modelName.includes('model1')) {
        return { info: { resolution: 768 } }
      }
      return { info: { resolution: 512 } }
    })
  })

  it('should return models grouped by type', () => {
    expect(store.modelsByType.checkpoints.length).toBe(2)
    expect(store.modelsByType.loras.length).toBe(1)
  })

  it('should filter models by name', () => {
    store.searchTerm = 'model1'
    expect(store.filteredModels.checkpoints.length).toBe(1)
    expect(store.filteredModels.checkpoints[0].name).toBe('model1.safetensors')
  })
})

Mocking Dependencies in Stores

Mocking API and other dependencies:

// Example from: tests-ui/tests/store/workflowStore.test.ts
// Add mock for api at the top of the file
vi.mock('@/scripts/api', () => ({
  api: {
    getUserData: vi.fn(),
    storeUserData: vi.fn(),
    listUserDataFullInfo: vi.fn(),
    apiURL: vi.fn(),
    addEventListener: vi.fn()
  }
}))

// Mock comfyApp globally for the store setup
vi.mock('@/scripts/app', () => ({
  app: {
    canvas: null // Start with canvas potentially undefined or null
  }
}))

describe('syncWorkflows', () => {
  const syncRemoteWorkflows = async (filenames: string[]) => {
    vi.mocked(api.listUserDataFullInfo).mockResolvedValue(
      filenames.map((filename) => ({
        path: filename,
        modified: new Date().getTime(),
        size: 1 // size !== -1 for remote workflows
      }))
    )
    return await store.syncWorkflows()
  }

  it('should sync workflows', async () => {
    await syncRemoteWorkflows(['a.json', 'b.json'])
    expect(store.workflows.length).toBe(2)
  })
})

Testing Store Watchers

Testing store watchers and reactive behavior:

// Example from: tests-ui/tests/store/workflowStore.test.ts
import { nextTick } from 'vue'

describe('Subgraphs', () => {
  it('should update automatically when activeWorkflow changes', async () => {
    // Arrange: Set initial canvas state
    const initialSubgraph = {
      name: 'Initial Subgraph',
      pathToRootGraph: [{ name: 'Root' }, { name: 'Initial Subgraph' }],
      isRootGraph: false
    }
    vi.mocked(comfyApp.canvas).subgraph = initialSubgraph as any

    // Trigger initial update
    store.updateActiveGraph()
    await nextTick()

    // Verify initial state
    expect(store.isSubgraphActive).toBe(true)
    expect(store.subgraphNamePath).toEqual(['Initial Subgraph'])

    // Act: Change the active workflow and canvas state
    const workflow2 = store.createTemporary('workflow2.json')
    vi.spyOn(workflow2, 'load').mockImplementation(async () => {
      workflow2.changeTracker = { activeState: {} } as any
      workflow2.originalContent = '{}'
      workflow2.content = '{}'
      return workflow2 as LoadedComfyWorkflow
    })

    // Change canvas state
    vi.mocked(comfyApp.canvas).subgraph = undefined

    await store.openWorkflow(workflow2)
    await nextTick() // Allow watcher to trigger

    // Assert: Check state was updated by the watcher
    expect(store.isSubgraphActive).toBe(false)
    expect(store.subgraphNamePath).toEqual([])
  })
})

Testing Store Integration

Testing store integration with other parts of the application:

// Example from: tests-ui/tests/store/workflowStore.test.ts
describe('renameWorkflow', () => {
  it('should rename workflow and update bookmarks', async () => {
    const workflow = store.createTemporary('dir/test.json')
    const bookmarkStore = useWorkflowBookmarkStore()

    // Set up initial bookmark
    expect(workflow.path).toBe('workflows/dir/test.json')
    await bookmarkStore.setBookmarked(workflow.path, true)
    expect(bookmarkStore.isBookmarked(workflow.path)).toBe(true)

    // Mock super.rename
    vi.spyOn(Object.getPrototypeOf(workflow), 'rename').mockImplementation(
      async function (this: any, newPath: string) {
        this.path = newPath
        return this
      } as any
    )

    // Perform rename
    const newPath = 'workflows/dir/renamed.json'
    await store.renameWorkflow(workflow, newPath)

    // Check that bookmark was transferred
    expect(bookmarkStore.isBookmarked(newPath)).toBe(true)
    expect(bookmarkStore.isBookmarked('workflows/dir/test.json')).toBe(false)
  })
})