mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
[Test] Run unittest with vitest (#2779)
This commit is contained in:
17
.github/workflows/test-ui.yaml
vendored
17
.github/workflows/test-ui.yaml
vendored
@@ -56,23 +56,6 @@ jobs:
|
||||
ComfyUI_frontend
|
||||
key: comfyui-setup-${{ steps.cache-key.outputs.key }}
|
||||
|
||||
jest-tests:
|
||||
needs: setup
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Restore cached setup
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
ComfyUI
|
||||
ComfyUI_frontend
|
||||
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
|
||||
|
||||
- name: Run Jest tests
|
||||
run: |
|
||||
npm run test:jest -- --verbose
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
playwright-tests:
|
||||
needs: setup
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/vitest.yaml
vendored
4
.github/workflows/vitest.yaml
vendored
@@ -22,4 +22,6 @@ jobs:
|
||||
run: npm ci
|
||||
|
||||
- name: Run Vitest tests
|
||||
run: npm run test:component
|
||||
run: |
|
||||
npm run test:component
|
||||
npm run test:unit
|
||||
|
||||
@@ -547,7 +547,7 @@ navigate to `http://<server_ip>:5173` (e.g. `http://192.168.2.20:5173` here), to
|
||||
### Unit Test
|
||||
|
||||
- `npm i` to install all dependencies
|
||||
- `npm run test:jest` to execute all unit tests.
|
||||
- `npm run test:unit` to execute all unit tests.
|
||||
|
||||
### Component Test
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/preset-env"
|
||||
],
|
||||
"plugins": [
|
||||
"babel-plugin-transform-import-meta"
|
||||
]
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import type { JestConfigWithTsJest } from 'ts-jest'
|
||||
|
||||
const jestConfig: JestConfigWithTsJest = {
|
||||
testMatch: ['**/tests-ui/**/*.test.ts'],
|
||||
testEnvironment: 'jsdom',
|
||||
moduleFileExtensions: ['js', 'jsx', 'json', 'vue', 'ts', 'tsx'],
|
||||
transform: {
|
||||
'^.+\\.vue$': '@vue/vue3-jest',
|
||||
'^.+\\.m?[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: './tsconfig.json',
|
||||
babelConfig: './babel.config.json'
|
||||
}
|
||||
]
|
||||
},
|
||||
transformIgnorePatterns: ['/node_modules/(?!(three|@three)/)'],
|
||||
moduleNameMapper: {
|
||||
'^@/(.*)$': '<rootDir>/src/$1',
|
||||
'\\.(css|less|scss|sass)$': 'identity-obj-proxy'
|
||||
},
|
||||
clearMocks: true,
|
||||
resetModules: true,
|
||||
setupFiles: ['./tests-ui/tests/globalSetup.ts']
|
||||
}
|
||||
|
||||
export default jestConfig
|
||||
3948
package-lock.json
generated
3948
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,8 +19,8 @@
|
||||
"typecheck": "vue-tsc --noEmit && tsc --noEmit && tsc-strict",
|
||||
"format": "prettier --write './**/*.{js,ts,tsx,vue,mts}'",
|
||||
"format:check": "prettier --check './**/*.{js,ts,tsx,vue,mts}'",
|
||||
"test:jest": "jest --config jest.config.ts",
|
||||
"test:browser": "npx playwright test",
|
||||
"test:unit": "vitest run tests-ui/tests",
|
||||
"test:component": "vitest run src/components/",
|
||||
"prepare": "husky || true",
|
||||
"preview": "vite preview",
|
||||
@@ -39,13 +39,11 @@
|
||||
"@pinia/testing": "^0.1.5",
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.0",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/lodash": "^4.17.6",
|
||||
"@types/node": "^20.14.8",
|
||||
"@types/three": "^0.169.0",
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@vue/vue3-jest": "^29.2.6",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"babel-plugin-transform-import-meta": "^2.2.1",
|
||||
"babel-plugin-transform-rename-import": "^2.3.0",
|
||||
@@ -58,13 +56,10 @@
|
||||
"happy-dom": "^15.11.0",
|
||||
"husky": "^9.0.11",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"lint-staged": "^15.2.7",
|
||||
"postcss": "^8.4.39",
|
||||
"prettier": "^3.3.2",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"ts-jest": "^29.1.4",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.15.6",
|
||||
"typescript": "^5.4.5",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { mount } from '@vue/test-utils'
|
||||
import Badge from 'primevue/badge'
|
||||
import PrimeVue from 'primevue/config'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
|
||||
import { createApp } from 'vue'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import PrimeVue from 'primevue/config'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import Textarea from 'primevue/textarea'
|
||||
import Tooltip from 'primevue/tooltip'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
import enMesages from '@/locales/en/main.json'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import PrimeVue from 'primevue/config'
|
||||
import Galleria from 'primevue/galleria'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import ComfyImage from '@/components/common/ComfyImage.vue'
|
||||
import { ResultItemImpl } from '@/stores/queueStore'
|
||||
|
||||
@@ -921,7 +921,7 @@ export class ComfyApp {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the impl after groupNode jest tests are removed.
|
||||
* Remove the impl after groupNode unit tests are removed.
|
||||
* @deprecated Use useWidgetStore().getWidgetType instead
|
||||
*/
|
||||
getWidgetType(inputData, inputName: string) {
|
||||
|
||||
@@ -275,7 +275,7 @@ export const useWorkflowService = () => {
|
||||
* a new graph.
|
||||
*/
|
||||
const beforeLoadNewGraph = () => {
|
||||
// Use workspaceStore here as it is patched in jest tests.
|
||||
// Use workspaceStore here as it is patched in unit tests.
|
||||
const workflowStore = useWorkspaceStore().workflow
|
||||
const activeWorkflow = workflowStore.activeWorkflow
|
||||
if (activeWorkflow) {
|
||||
@@ -298,7 +298,7 @@ export const useWorkflowService = () => {
|
||||
value: string | ComfyWorkflow | null,
|
||||
workflowData: ComfyWorkflowJSON
|
||||
) => {
|
||||
// Use workspaceStore here as it is patched in jest tests.
|
||||
// Use workspaceStore here as it is patched in unit tests.
|
||||
const workflowStore = useWorkspaceStore().workflow
|
||||
if (typeof value === 'string') {
|
||||
const workflow = workflowStore.getWorkflowByPath(
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// @ts-strict-ignore
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
type ComfyNodeDef,
|
||||
validateComfyNodeDef
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// @ts-strict-ignore
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { adjustColor } from '@/utils/colorUtil'
|
||||
|
||||
interface ColorTestCase {
|
||||
@@ -14,7 +16,7 @@ interface ColorTestCase {
|
||||
|
||||
type ColorFormat = 'hex' | 'rgb' | 'rgba' | 'hsl' | 'hsla'
|
||||
|
||||
jest.mock('lodash', () => ({
|
||||
vi.mock('lodash', () => ({
|
||||
memoize: (fn: any) => fn
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// @ts-strict-ignore
|
||||
import fs from 'fs'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { validateComfyWorkflow } from '@/schemas/comfyWorkflowSchema'
|
||||
import { defaultGraph } from '@/scripts/defaultGraph'
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { useComboWidget } from '@/composables/widgets/useComboWidget'
|
||||
import type { InputSpec } from '@/schemas/nodeDefSchema'
|
||||
|
||||
jest.mock('@/scripts/widgets', () => ({
|
||||
addValueControlWidgets: jest.fn()
|
||||
vi.mock('@/stores/widgetStore', () => ({
|
||||
useWidgetStore: () => ({
|
||||
getDefaultValue: vi.fn()
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/scripts/widgets', () => ({
|
||||
addValueControlWidgets: vi.fn()
|
||||
}))
|
||||
|
||||
describe('useComboWidget', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should handle undefined spec', () => {
|
||||
const constructor = useComboWidget()
|
||||
const mockNode = {
|
||||
addWidget: jest.fn().mockReturnValue({ options: {} } as any)
|
||||
addWidget: vi.fn().mockReturnValue({ options: {} } as any)
|
||||
}
|
||||
|
||||
const inputSpec: InputSpec = ['COMBO', undefined]
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { _for_testing } from '@/composables/widgets/useFloatWidget'
|
||||
|
||||
jest.mock('@/scripts/widgets', () => ({
|
||||
addValueControlWidgets: jest.fn()
|
||||
vi.mock('@/scripts/widgets', () => ({
|
||||
addValueControlWidgets: vi.fn()
|
||||
}))
|
||||
|
||||
jest.mock('@/stores/settingStore', () => ({
|
||||
vi.mock('@/stores/settingStore', () => ({
|
||||
useSettingStore: () => ({
|
||||
settings: {}
|
||||
})
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { _for_testing } from '@/composables/widgets/useIntWidget'
|
||||
|
||||
jest.mock('@/scripts/widgets', () => ({
|
||||
addValueControlWidgets: jest.fn()
|
||||
vi.mock('@/scripts/widgets', () => ({
|
||||
addValueControlWidgets: vi.fn()
|
||||
}))
|
||||
|
||||
jest.mock('@/stores/settingStore', () => ({
|
||||
vi.mock('@/stores/settingStore', () => ({
|
||||
useSettingStore: () => ({
|
||||
settings: {}
|
||||
})
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import axios from 'axios'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { useRemoteWidget } from '@/composables/widgets/useRemoteWidget'
|
||||
import type { ComboInputSpecV2 } from '@/schemas/nodeDefSchema'
|
||||
|
||||
jest.mock('axios', () => ({
|
||||
get: jest.fn()
|
||||
}))
|
||||
vi.mock('axios', () => {
|
||||
return {
|
||||
default: {
|
||||
get: vi.fn()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
jest.mock('@/i18n', () => ({
|
||||
vi.mock('@/i18n', () => ({
|
||||
i18n: {
|
||||
global: {
|
||||
t: jest.fn((key) => key)
|
||||
t: vi.fn((key) => key)
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
jest.mock('@/stores/settingStore', () => ({
|
||||
vi.mock('@/stores/settingStore', () => ({
|
||||
useSettingStore: () => ({
|
||||
settings: {}
|
||||
})
|
||||
@@ -46,12 +51,12 @@ const createMockOptions = (inputOverrides = {}) => ({
|
||||
})
|
||||
|
||||
function mockAxiosResponse(data: unknown, status = 200) {
|
||||
jest.mocked(axios.get).mockResolvedValueOnce({ data, status })
|
||||
vi.mocked(axios.get).mockResolvedValueOnce({ data, status })
|
||||
}
|
||||
|
||||
function mockAxiosError(error: Error | string) {
|
||||
const err = error instanceof Error ? error : new Error(error)
|
||||
jest.mocked(axios.get).mockRejectedValueOnce(err)
|
||||
vi.mocked(axios.get).mockRejectedValueOnce(err)
|
||||
}
|
||||
|
||||
function createHookWithData(data: unknown, inputOverrides = {}) {
|
||||
@@ -79,19 +84,19 @@ describe('useRemoteWidget', () => {
|
||||
let mockInputData: ComboInputSpecV2
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
// Reset mocks
|
||||
jest.mocked(axios.get).mockReset()
|
||||
vi.mocked(axios.get).mockReset()
|
||||
// Reset cache between tests
|
||||
jest.spyOn(Map.prototype, 'get').mockClear()
|
||||
jest.spyOn(Map.prototype, 'set').mockClear()
|
||||
jest.spyOn(Map.prototype, 'delete').mockClear()
|
||||
vi.spyOn(Map.prototype, 'get').mockClear()
|
||||
vi.spyOn(Map.prototype, 'set').mockClear()
|
||||
vi.spyOn(Map.prototype, 'delete').mockClear()
|
||||
|
||||
mockInputData = createMockInputData()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('initialization', () => {
|
||||
@@ -124,7 +129,7 @@ describe('useRemoteWidget', () => {
|
||||
const mockData = ['optionA', 'optionB']
|
||||
const { hook, result } = await setupHookWithResponse(mockData)
|
||||
expect(result).toEqual(mockData)
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledWith(
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledWith(
|
||||
hook.cacheKey.split(';')[0], // Get the route part from cache key
|
||||
expect.any(Object)
|
||||
)
|
||||
@@ -187,12 +192,12 @@ describe('useRemoteWidget', () => {
|
||||
|
||||
describe('refresh behavior', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers()
|
||||
vi.useFakeTimers()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers()
|
||||
jest.clearAllMocks()
|
||||
vi.useRealTimers()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('permanent widgets (no refresh)', () => {
|
||||
@@ -203,7 +208,7 @@ describe('useRemoteWidget', () => {
|
||||
await getResolvedValue(hook)
|
||||
await getResolvedValue(hook)
|
||||
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('permanent widgets should re-fetch if refreshValue is called', async () => {
|
||||
@@ -224,12 +229,12 @@ describe('useRemoteWidget', () => {
|
||||
|
||||
const hook = useRemoteWidget(createMockOptions())
|
||||
await getResolvedValue(hook)
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
|
||||
jest.setSystemTime(Date.now() + FIRST_BACKOFF)
|
||||
vi.setSystemTime(Date.now() + FIRST_BACKOFF)
|
||||
const secondData = await getResolvedValue(hook)
|
||||
expect(secondData).toBe('Loading...')
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(2)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('should treat empty refresh field as permanent', async () => {
|
||||
@@ -238,7 +243,7 @@ describe('useRemoteWidget', () => {
|
||||
await getResolvedValue(hook)
|
||||
await getResolvedValue(hook)
|
||||
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -250,11 +255,11 @@ describe('useRemoteWidget', () => {
|
||||
const { hook } = await setupHookWithResponse(mockData1, { refresh })
|
||||
mockAxiosResponse(mockData2)
|
||||
|
||||
jest.setSystemTime(Date.now() + refresh)
|
||||
vi.setSystemTime(Date.now() + refresh)
|
||||
const newData = await getResolvedValue(hook)
|
||||
|
||||
expect(newData).toEqual(mockData2)
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(2)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('should not refresh when data is not stale', async () => {
|
||||
@@ -262,10 +267,10 @@ describe('useRemoteWidget', () => {
|
||||
refresh: 512
|
||||
})
|
||||
|
||||
jest.setSystemTime(Date.now() + 128)
|
||||
vi.setSystemTime(Date.now() + 128)
|
||||
await getResolvedValue(hook)
|
||||
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should use backoff instead of refresh after error', async () => {
|
||||
@@ -275,15 +280,15 @@ describe('useRemoteWidget', () => {
|
||||
})
|
||||
|
||||
mockAxiosError('Network error')
|
||||
jest.setSystemTime(Date.now() + refresh)
|
||||
vi.setSystemTime(Date.now() + refresh)
|
||||
await getResolvedValue(hook)
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(2)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(2)
|
||||
|
||||
mockAxiosResponse(['second success'])
|
||||
jest.setSystemTime(Date.now() + FIRST_BACKOFF)
|
||||
vi.setSystemTime(Date.now() + FIRST_BACKOFF)
|
||||
const thirdData = await getResolvedValue(hook)
|
||||
expect(thirdData).toEqual(['second success'])
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(3)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(3)
|
||||
})
|
||||
|
||||
it('should use last valid value after error', async () => {
|
||||
@@ -293,21 +298,21 @@ describe('useRemoteWidget', () => {
|
||||
})
|
||||
|
||||
mockAxiosError('Network error')
|
||||
jest.setSystemTime(Date.now() + refresh)
|
||||
vi.setSystemTime(Date.now() + refresh)
|
||||
const secondData = await getResolvedValue(hook)
|
||||
|
||||
expect(secondData).toEqual(['a valid value'])
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(2)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('error handling and backoff', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers()
|
||||
vi.useFakeTimers()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('should implement exponential backoff on errors', async () => {
|
||||
@@ -319,15 +324,15 @@ describe('useRemoteWidget', () => {
|
||||
expect(entry1?.error).toBeTruthy()
|
||||
|
||||
await getResolvedValue(hook)
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
|
||||
jest.setSystemTime(Date.now() + 500)
|
||||
vi.setSystemTime(Date.now() + 500)
|
||||
await getResolvedValue(hook)
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(1) // Still backing off
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(1) // Still backing off
|
||||
|
||||
jest.setSystemTime(Date.now() + 3000)
|
||||
vi.setSystemTime(Date.now() + 3000)
|
||||
await getResolvedValue(hook)
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(2)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(2)
|
||||
expect(entry1?.data).toBeDefined()
|
||||
})
|
||||
|
||||
@@ -337,7 +342,7 @@ describe('useRemoteWidget', () => {
|
||||
const firstData = await getResolvedValue(hook)
|
||||
expect(firstData).toBe('Loading...')
|
||||
|
||||
jest.setSystemTime(Date.now() + 3000)
|
||||
vi.setSystemTime(Date.now() + 3000)
|
||||
mockAxiosResponse(['option1'])
|
||||
const secondData = await getResolvedValue(hook)
|
||||
expect(secondData).toEqual(['option1'])
|
||||
@@ -354,7 +359,7 @@ describe('useRemoteWidget', () => {
|
||||
const entry1 = hook.getCacheEntry()
|
||||
expect(entry1?.error).toBeTruthy()
|
||||
|
||||
jest.setSystemTime(Date.now() + 3000)
|
||||
vi.setSystemTime(Date.now() + 3000)
|
||||
mockAxiosResponse(['success after backoff'])
|
||||
const secondData = await getResolvedValue(hook)
|
||||
expect(secondData).toEqual(['success after backoff'])
|
||||
@@ -373,17 +378,17 @@ describe('useRemoteWidget', () => {
|
||||
const entry1 = hook.getCacheEntry()
|
||||
expect(entry1?.error).toBeTruthy()
|
||||
|
||||
jest.setSystemTime(Date.now() + 3000)
|
||||
vi.setSystemTime(Date.now() + 3000)
|
||||
const secondData = await getResolvedValue(hook)
|
||||
expect(secondData).toBe('Loading...')
|
||||
expect(entry1?.error).toBeDefined()
|
||||
|
||||
jest.setSystemTime(Date.now() + 9000)
|
||||
vi.setSystemTime(Date.now() + 9000)
|
||||
const thirdData = await getResolvedValue(hook)
|
||||
expect(thirdData).toBe('Loading...')
|
||||
expect(entry1?.error).toBeDefined()
|
||||
|
||||
jest.setSystemTime(Date.now() + 120_000)
|
||||
vi.setSystemTime(Date.now() + 120_000)
|
||||
mockAxiosResponse(['success after multiple backoffs'])
|
||||
const fourthData = await getResolvedValue(hook)
|
||||
expect(fourthData).toEqual(['success after multiple backoffs'])
|
||||
@@ -405,7 +410,7 @@ describe('useRemoteWidget', () => {
|
||||
|
||||
it('should prevent duplicate in-flight requests', async () => {
|
||||
const promise = Promise.resolve({ data: ['non-duplicate'] })
|
||||
jest.mocked(axios.get).mockImplementationOnce(() => promise)
|
||||
vi.mocked(axios.get).mockImplementationOnce(() => promise)
|
||||
|
||||
const hook = useRemoteWidget(createMockOptions())
|
||||
const [result1, result2] = await Promise.all([
|
||||
@@ -414,7 +419,7 @@ describe('useRemoteWidget', () => {
|
||||
])
|
||||
|
||||
expect(result1).toBe(result2)
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -433,7 +438,7 @@ describe('useRemoteWidget', () => {
|
||||
|
||||
expect(data1).toEqual(['shared data'])
|
||||
expect(data2).toEqual(['shared data'])
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
expect(hook1.getCachedValue()).toBe(hook2.getCachedValue())
|
||||
})
|
||||
|
||||
@@ -454,7 +459,7 @@ describe('useRemoteWidget', () => {
|
||||
expect(data2).toBe(data1)
|
||||
expect(data3).toBe(data1)
|
||||
expect(data4).toBe(data1)
|
||||
expect(jest.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
expect(vi.mocked(axios.get)).toHaveBeenCalledTimes(1)
|
||||
expect(hook1.getCachedValue()).toBe(hook2.getCachedValue())
|
||||
expect(hook2.getCachedValue()).toBe(hook3.getCachedValue())
|
||||
expect(hook3.getCachedValue()).toBe(hook4.getCachedValue())
|
||||
@@ -466,7 +471,7 @@ describe('useRemoteWidget', () => {
|
||||
resolvePromise = resolve
|
||||
})
|
||||
|
||||
jest.mocked(axios.get).mockImplementationOnce(() => delayedPromise)
|
||||
vi.mocked(axios.get).mockImplementationOnce(() => delayedPromise)
|
||||
|
||||
const hook = useRemoteWidget(createMockOptions())
|
||||
hook.getValue()
|
||||
@@ -487,7 +492,7 @@ describe('useRemoteWidget', () => {
|
||||
resolvePromise = resolve
|
||||
})
|
||||
|
||||
jest.mocked(axios.get).mockImplementationOnce(() => delayedPromise)
|
||||
vi.mocked(axios.get).mockImplementationOnce(() => delayedPromise)
|
||||
|
||||
let hook = useRemoteWidget(createMockOptions())
|
||||
const fetchPromise = hook.getValue()
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { afterEach, describe, expect, it, test, vi } from 'vitest'
|
||||
|
||||
import { processDynamicPrompt } from '@/utils/formatUtil'
|
||||
|
||||
describe('dynamic prompts', () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks()
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('handles single and multiline comments', () => {
|
||||
@@ -14,22 +16,22 @@ describe('dynamic prompts', () => {
|
||||
it('handles simple option groups', () => {
|
||||
const input = '{option1|option2}'
|
||||
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0)
|
||||
expect(processDynamicPrompt(input)).toBe('option1')
|
||||
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
expect(processDynamicPrompt(input)).toBe('option2')
|
||||
})
|
||||
|
||||
test('handles trailing empty options', () => {
|
||||
const input = '{a|}'
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
expect(processDynamicPrompt(input)).toBe('')
|
||||
})
|
||||
|
||||
test('handles leading empty options', () => {
|
||||
const input = '{|a}'
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0)
|
||||
expect(processDynamicPrompt(input)).toBe('')
|
||||
})
|
||||
|
||||
@@ -40,22 +42,22 @@ describe('dynamic prompts', () => {
|
||||
|
||||
test('handles multiple nested empty alternatives', () => {
|
||||
const input = '{a|{b||c}|}'
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.5)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.5)
|
||||
expect(processDynamicPrompt(input)).toBe('')
|
||||
})
|
||||
|
||||
test('handles unescaped special characters gracefully', () => {
|
||||
const input = '{a|\\}'
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
expect(processDynamicPrompt(input)).toBe('}')
|
||||
})
|
||||
|
||||
it('handles nested option groups', () => {
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0) // pick the first option at each level
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0) // pick the first option at each level
|
||||
const input = '{a|{b|{c|d}}}'
|
||||
expect(processDynamicPrompt(input)).toBe('a')
|
||||
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.99) // pick the last option at each level
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.99) // pick the last option at each level
|
||||
expect(processDynamicPrompt(input)).toBe('d')
|
||||
})
|
||||
|
||||
@@ -76,46 +78,46 @@ describe('dynamic prompts', () => {
|
||||
|
||||
test('handles deeply nested escaped characters', () => {
|
||||
const input = '{a|{b|\\{c\\}}}'
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
expect(processDynamicPrompt(input)).toBe('{c}')
|
||||
})
|
||||
|
||||
it('handles mixed input', () => {
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
const input =
|
||||
'<{option1|option2}>/*comment*/ ({something|else}:2) \\{escaped\\}!'
|
||||
expect(processDynamicPrompt(input)).toBe('<option2> (else:2) {escaped}!')
|
||||
})
|
||||
|
||||
it('handles non-paired braces gracefully', () => {
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0)
|
||||
const input = '{option1|option2|{nested1|nested2'
|
||||
expect(processDynamicPrompt(input)).toBe('option1')
|
||||
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.4)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.4)
|
||||
expect(processDynamicPrompt(input)).toBe('option2')
|
||||
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
expect(processDynamicPrompt(input)).toBe('nested2')
|
||||
})
|
||||
|
||||
it('handles deep nesting', () => {
|
||||
const input = '{a|{b|{c|{d|{e|{f|{g}}1}2}3}4}5}'
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.99)
|
||||
expect(processDynamicPrompt(input)).toBe('g12345')
|
||||
})
|
||||
|
||||
test('handles empty alternative inside braces', () => {
|
||||
const input = '{|a||b|}'
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0)
|
||||
expect(processDynamicPrompt(input)).toBe('')
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.3)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.3)
|
||||
expect(processDynamicPrompt(input)).toBe('a')
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.5)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.5)
|
||||
expect(processDynamicPrompt(input)).toBe('')
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.75)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.75)
|
||||
expect(processDynamicPrompt(input)).toBe('b')
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.999)
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.999)
|
||||
expect(processDynamicPrompt(input)).toBe('')
|
||||
})
|
||||
|
||||
@@ -130,7 +132,7 @@ describe('dynamic prompts', () => {
|
||||
})
|
||||
|
||||
test('handles complex mixed cases', () => {
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.5) //pick the second option from each group
|
||||
vi.spyOn(Math, 'random').mockReturnValue(0.5) //pick the second option from each group
|
||||
const input = '1{a|b|{c|d}}2{e|f}3'
|
||||
expect(processDynamicPrompt(input)).toBe('1b2f3')
|
||||
})
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
module.exports = async function () {
|
||||
jest.mock('vue-i18n', () => {
|
||||
return {
|
||||
useI18n: jest.fn()
|
||||
}
|
||||
})
|
||||
|
||||
jest.mock('jsondiffpatch', () => {
|
||||
return {
|
||||
diff: jest.fn()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { LiteGraph } from '@comfyorg/litegraph'
|
||||
import { LGraph } from '@comfyorg/litegraph'
|
||||
import { LGraphNode } from '@comfyorg/litegraph'
|
||||
import { LGraph, LGraphNode, LiteGraph } from '@comfyorg/litegraph'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
function swapNodes(nodes: LGraphNode[]) {
|
||||
const firstNode = nodes[0]
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// @ts-strict-ignore
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
BooleanInputSpec,
|
||||
ComfyInputsSpec,
|
||||
@@ -8,8 +10,6 @@ import {
|
||||
StringInputSpec
|
||||
} from '@/stores/nodeDefStore'
|
||||
|
||||
// Adjust the import path as needed
|
||||
|
||||
describe('ComfyInputsSpec', () => {
|
||||
it('should transform a plain object to ComfyInputsSpec instance', () => {
|
||||
const plainObject = {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// @ts-strict-ignore
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { NodeSearchService } from '@/services/nodeSearchService'
|
||||
import { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { NodeSourceType, getNodeSource } from '@/types/nodeSource'
|
||||
|
||||
describe('getNodeSource', () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import { KeybindingImpl, useKeybindingStore } from '@/stores/keybindingStore'
|
||||
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { api } from '@/scripts/api'
|
||||
import { useModelStore } from '@/stores/modelStore'
|
||||
|
||||
// Mock the api
|
||||
jest.mock('@/scripts/api', () => ({
|
||||
vi.mock('@/scripts/api', () => ({
|
||||
api: {
|
||||
getModels: jest.fn(),
|
||||
getModelFolders: jest.fn(),
|
||||
viewMetadata: jest.fn()
|
||||
getModels: vi.fn(),
|
||||
getModelFolders: vi.fn(),
|
||||
viewMetadata: vi.fn()
|
||||
}
|
||||
}))
|
||||
|
||||
function enableMocks() {
|
||||
;(api.getModels as jest.Mock).mockResolvedValue([
|
||||
vi.mocked(api.getModels).mockResolvedValue([
|
||||
{ name: 'sdxl.safetensors', pathIndex: 0 },
|
||||
{ name: 'sdv15.safetensors', pathIndex: 0 },
|
||||
{ name: 'noinfo.safetensors', pathIndex: 0 }
|
||||
])
|
||||
;(api.getModelFolders as jest.Mock).mockResolvedValue([
|
||||
vi.mocked(api.getModelFolders).mockResolvedValue([
|
||||
{ name: 'checkpoints', folders: ['/path/to/checkpoints'] },
|
||||
{ name: 'vae', folders: ['/path/to/vae'] }
|
||||
])
|
||||
;(api.viewMetadata as jest.Mock).mockImplementation((_, model) => {
|
||||
vi.mocked(api.viewMetadata).mockImplementation((_, model) => {
|
||||
if (model === 'noinfo.safetensors') {
|
||||
return Promise.resolve({})
|
||||
}
|
||||
@@ -46,6 +47,7 @@ describe('useModelStore', () => {
|
||||
beforeEach(async () => {
|
||||
setActivePinia(createPinia())
|
||||
store = useModelStore()
|
||||
vi.resetAllMocks()
|
||||
})
|
||||
|
||||
it('should load models', async () => {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// @ts-strict-ignore
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { TaskItemImpl } from '@/stores/queueStore'
|
||||
|
||||
describe('TaskItemImpl', () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import { ServerConfig } from '@/constants/serverConfig'
|
||||
import { useServerConfigStore } from '@/stores/serverConfigStore'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { api } from '@/scripts/api'
|
||||
import { app } from '@/scripts/app'
|
||||
@@ -6,19 +7,19 @@ import { getSettingInfo, useSettingStore } from '@/stores/settingStore'
|
||||
import type { SettingParams } from '@/types/settingTypes'
|
||||
|
||||
// Mock the api
|
||||
jest.mock('@/scripts/api', () => ({
|
||||
vi.mock('@/scripts/api', () => ({
|
||||
api: {
|
||||
getSettings: jest.fn(),
|
||||
storeSetting: jest.fn()
|
||||
getSettings: vi.fn(),
|
||||
storeSetting: vi.fn()
|
||||
}
|
||||
}))
|
||||
|
||||
// Mock the app
|
||||
jest.mock('@/scripts/app', () => ({
|
||||
vi.mock('@/scripts/app', () => ({
|
||||
app: {
|
||||
ui: {
|
||||
settings: {
|
||||
dispatchChange: jest.fn()
|
||||
dispatchChange: vi.fn()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +31,7 @@ describe('useSettingStore', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
store = useSettingStore()
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should initialize with empty settings', () => {
|
||||
@@ -42,7 +43,7 @@ describe('useSettingStore', () => {
|
||||
describe('loadSettingValues', () => {
|
||||
it('should load settings from API', async () => {
|
||||
const mockSettings = { 'test.setting': 'value' }
|
||||
;(api.getSettings as jest.Mock).mockResolvedValue(mockSettings)
|
||||
vi.mocked(api.getSettings).mockResolvedValue(mockSettings as any)
|
||||
|
||||
await store.loadSettingValues()
|
||||
|
||||
@@ -123,8 +124,8 @@ describe('useSettingStore', () => {
|
||||
})
|
||||
|
||||
it('should set value and trigger onChange', async () => {
|
||||
const onChangeMock = jest.fn()
|
||||
const dispatchChangeMock = app.ui.settings.dispatchChange as jest.Mock
|
||||
const onChangeMock = vi.fn()
|
||||
const dispatchChangeMock = vi.mocked(app.ui.settings.dispatchChange)
|
||||
const setting: SettingParams = {
|
||||
id: 'test.setting',
|
||||
name: 'test.setting',
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { api } from '@/scripts/api'
|
||||
import { UserFile, useUserFileStore } from '@/stores/userFileStore'
|
||||
|
||||
// Mock the api
|
||||
jest.mock('@/scripts/api', () => ({
|
||||
vi.mock('@/scripts/api', () => ({
|
||||
api: {
|
||||
listUserDataFullInfo: jest.fn(),
|
||||
getUserData: jest.fn(),
|
||||
storeUserData: jest.fn(),
|
||||
deleteUserData: jest.fn(),
|
||||
moveUserData: jest.fn()
|
||||
listUserDataFullInfo: vi.fn(),
|
||||
getUserData: vi.fn(),
|
||||
storeUserData: vi.fn(),
|
||||
deleteUserData: vi.fn(),
|
||||
moveUserData: vi.fn()
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -20,6 +21,7 @@ describe('useUserFileStore', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
store = useUserFileStore()
|
||||
vi.resetAllMocks()
|
||||
})
|
||||
|
||||
it('should initialize with empty files', () => {
|
||||
@@ -34,7 +36,7 @@ describe('useUserFileStore', () => {
|
||||
{ path: 'file1.txt', modified: 123, size: 100 },
|
||||
{ path: 'file2.txt', modified: 456, size: 200 }
|
||||
]
|
||||
;(api.listUserDataFullInfo as jest.Mock).mockResolvedValue(mockFiles)
|
||||
vi.mocked(api.listUserDataFullInfo).mockResolvedValue(mockFiles)
|
||||
|
||||
await store.syncFiles('dir')
|
||||
|
||||
@@ -45,11 +47,11 @@ describe('useUserFileStore', () => {
|
||||
|
||||
it('should update existing files', async () => {
|
||||
const initialFile = { path: 'file1.txt', modified: 123, size: 100 }
|
||||
;(api.listUserDataFullInfo as jest.Mock).mockResolvedValue([initialFile])
|
||||
vi.mocked(api.listUserDataFullInfo).mockResolvedValue([initialFile])
|
||||
await store.syncFiles('dir')
|
||||
|
||||
const updatedFile = { path: 'file1.txt', modified: 456, size: 200 }
|
||||
;(api.listUserDataFullInfo as jest.Mock).mockResolvedValue([updatedFile])
|
||||
vi.mocked(api.listUserDataFullInfo).mockResolvedValue([updatedFile])
|
||||
await store.syncFiles('dir')
|
||||
|
||||
expect(store.userFiles).toHaveLength(1)
|
||||
@@ -62,11 +64,11 @@ describe('useUserFileStore', () => {
|
||||
{ path: 'file1.txt', modified: 123, size: 100 },
|
||||
{ path: 'file2.txt', modified: 456, size: 200 }
|
||||
]
|
||||
;(api.listUserDataFullInfo as jest.Mock).mockResolvedValue(initialFiles)
|
||||
vi.mocked(api.listUserDataFullInfo).mockResolvedValue(initialFiles)
|
||||
await store.syncFiles('dir')
|
||||
|
||||
const updatedFiles = [{ path: 'file1.txt', modified: 123, size: 100 }]
|
||||
;(api.listUserDataFullInfo as jest.Mock).mockResolvedValue(updatedFiles)
|
||||
vi.mocked(api.listUserDataFullInfo).mockResolvedValue(updatedFiles)
|
||||
await store.syncFiles('dir')
|
||||
|
||||
expect(store.userFiles).toHaveLength(1)
|
||||
@@ -75,7 +77,7 @@ describe('useUserFileStore', () => {
|
||||
|
||||
it('should sync root directory when no directory is specified', async () => {
|
||||
const mockFiles = [{ path: 'file1.txt', modified: 123, size: 100 }]
|
||||
;(api.listUserDataFullInfo as jest.Mock).mockResolvedValue(mockFiles)
|
||||
vi.mocked(api.listUserDataFullInfo).mockResolvedValue(mockFiles)
|
||||
|
||||
await store.syncFiles()
|
||||
|
||||
@@ -89,10 +91,10 @@ describe('useUserFileStore', () => {
|
||||
describe('load', () => {
|
||||
it('should load file content', async () => {
|
||||
const file = new UserFile('file1.txt', 123, 100)
|
||||
;(api.getUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.getUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
text: () => Promise.resolve('file content')
|
||||
})
|
||||
} as Response)
|
||||
|
||||
await file.load()
|
||||
|
||||
@@ -104,10 +106,10 @@ describe('useUserFileStore', () => {
|
||||
|
||||
it('should throw error on failed load', async () => {
|
||||
const file = new UserFile('file1.txt', 123, 100)
|
||||
;(api.getUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.getUserData).mockResolvedValue({
|
||||
status: 404,
|
||||
statusText: 'Not Found'
|
||||
})
|
||||
} as Response)
|
||||
|
||||
await expect(file.load()).rejects.toThrow(
|
||||
"Failed to load file 'file1.txt': 404 Not Found"
|
||||
@@ -120,10 +122,10 @@ describe('useUserFileStore', () => {
|
||||
const file = new UserFile('file1.txt', 123, 100)
|
||||
file.content = 'modified content'
|
||||
file.originalContent = 'original content'
|
||||
;(api.storeUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.storeUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
json: () => Promise.resolve({ modified: 456, size: 200 })
|
||||
})
|
||||
} as Response)
|
||||
|
||||
await file.save()
|
||||
|
||||
@@ -150,7 +152,9 @@ describe('useUserFileStore', () => {
|
||||
describe('delete', () => {
|
||||
it('should delete file', async () => {
|
||||
const file = new UserFile('file1.txt', 123, 100)
|
||||
;(api.deleteUserData as jest.Mock).mockResolvedValue({ status: 204 })
|
||||
vi.mocked(api.deleteUserData).mockResolvedValue({
|
||||
status: 204
|
||||
} as Response)
|
||||
|
||||
await file.delete()
|
||||
|
||||
@@ -161,10 +165,10 @@ describe('useUserFileStore', () => {
|
||||
describe('rename', () => {
|
||||
it('should rename file', async () => {
|
||||
const file = new UserFile('file1.txt', 123, 100)
|
||||
;(api.moveUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.moveUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
json: () => Promise.resolve({ modified: 456, size: 200 })
|
||||
})
|
||||
} as Response)
|
||||
|
||||
await file.rename('newfile.txt')
|
||||
|
||||
@@ -182,10 +186,10 @@ describe('useUserFileStore', () => {
|
||||
it('should save file with new path', async () => {
|
||||
const file = new UserFile('file1.txt', 123, 100)
|
||||
file.content = 'file content'
|
||||
;(api.storeUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.storeUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
json: () => Promise.resolve({ modified: 456, size: 200 })
|
||||
})
|
||||
} as Response)
|
||||
|
||||
const newFile = await file.saveAs('newfile.txt')
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { api } from '@/scripts/api'
|
||||
import { defaultGraph, defaultGraphJSON } from '@/scripts/defaultGraph'
|
||||
@@ -10,11 +11,11 @@ import {
|
||||
} from '@/stores/workflowStore'
|
||||
|
||||
// Add mock for api at the top of the file
|
||||
jest.mock('@/scripts/api', () => ({
|
||||
vi.mock('@/scripts/api', () => ({
|
||||
api: {
|
||||
getUserData: jest.fn(),
|
||||
storeUserData: jest.fn(),
|
||||
listUserDataFullInfo: jest.fn()
|
||||
getUserData: vi.fn(),
|
||||
storeUserData: vi.fn(),
|
||||
listUserDataFullInfo: vi.fn()
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -23,10 +24,10 @@ describe('useWorkflowStore', () => {
|
||||
let bookmarkStore: ReturnType<typeof useWorkflowBookmarkStore>
|
||||
|
||||
const syncRemoteWorkflows = async (filenames: string[]) => {
|
||||
;(api.listUserDataFullInfo as jest.Mock).mockResolvedValue(
|
||||
vi.mocked(api.listUserDataFullInfo).mockResolvedValue(
|
||||
filenames.map((filename) => ({
|
||||
path: filename,
|
||||
modified: new Date().toISOString(),
|
||||
modified: new Date().getTime(),
|
||||
size: 1 // size !== -1 for remote workflows
|
||||
}))
|
||||
)
|
||||
@@ -37,16 +38,16 @@ describe('useWorkflowStore', () => {
|
||||
setActivePinia(createPinia())
|
||||
store = useWorkflowStore()
|
||||
bookmarkStore = useWorkflowBookmarkStore()
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Add default mock implementations
|
||||
;(api.getUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.getUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
json: () => Promise.resolve({ favorites: [] })
|
||||
})
|
||||
;(api.storeUserData as jest.Mock).mockResolvedValue({
|
||||
} as Response)
|
||||
vi.mocked(api.storeUserData).mockResolvedValue({
|
||||
status: 200
|
||||
})
|
||||
} as Response)
|
||||
})
|
||||
|
||||
describe('syncWorkflows', () => {
|
||||
@@ -86,7 +87,7 @@ describe('useWorkflowStore', () => {
|
||||
const mockWorkflowData = { nodes: [], links: [] }
|
||||
|
||||
// Mock the load response
|
||||
jest.spyOn(workflow, 'load').mockImplementation(async () => {
|
||||
vi.spyOn(workflow, 'load').mockImplementation(async () => {
|
||||
workflow.changeTracker = { activeState: mockWorkflowData } as any
|
||||
return workflow as LoadedComfyWorkflow
|
||||
})
|
||||
@@ -103,7 +104,7 @@ describe('useWorkflowStore', () => {
|
||||
|
||||
it('should not reload an already active workflow', async () => {
|
||||
const workflow = await store.createTemporary('test.json').load()
|
||||
jest.spyOn(workflow, 'load')
|
||||
vi.spyOn(workflow, 'load')
|
||||
|
||||
// Set as active workflow
|
||||
store.activeWorkflow = workflow
|
||||
@@ -121,10 +122,10 @@ describe('useWorkflowStore', () => {
|
||||
expect(workflow.path).toBe('workflows/a.json')
|
||||
expect(workflow.isLoaded).toBe(false)
|
||||
expect(workflow.isTemporary).toBe(false)
|
||||
;(api.getUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.getUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
text: () => Promise.resolve(defaultGraphJSON)
|
||||
})
|
||||
} as Response)
|
||||
await workflow.load()
|
||||
|
||||
expect(workflow.isLoaded).toBe(true)
|
||||
@@ -142,10 +143,10 @@ describe('useWorkflowStore', () => {
|
||||
expect(workflow).not.toBeNull()
|
||||
expect(workflow.path).toBe('workflows/a.json')
|
||||
expect(workflow.isLoaded).toBe(false)
|
||||
;(api.getUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.getUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
text: () => Promise.resolve(defaultGraphJSON)
|
||||
})
|
||||
} as Response)
|
||||
|
||||
const loadedWorkflow = await store.openWorkflow(workflow)
|
||||
|
||||
@@ -175,10 +176,10 @@ describe('useWorkflowStore', () => {
|
||||
workflowA = store.getWorkflowByPath('workflows/a.json')!
|
||||
workflowB = store.getWorkflowByPath('workflows/b.json')!
|
||||
workflowC = store.getWorkflowByPath('workflows/c.json')!
|
||||
;(api.getUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.getUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
text: () => Promise.resolve(defaultGraphJSON)
|
||||
})
|
||||
} as Response)
|
||||
})
|
||||
|
||||
it('should open workflows adjacent to the active workflow', async () => {
|
||||
@@ -263,12 +264,12 @@ describe('useWorkflowStore', () => {
|
||||
expect(bookmarkStore.isBookmarked(workflow.path)).toBe(true)
|
||||
|
||||
// Mock super.rename
|
||||
jest
|
||||
.spyOn(Object.getPrototypeOf(workflow), 'rename')
|
||||
.mockImplementation(async function (this: any, newPath: string) {
|
||||
vi.spyOn(Object.getPrototypeOf(workflow), 'rename').mockImplementation(
|
||||
async function (this: any, newPath: string) {
|
||||
this.path = newPath
|
||||
return this
|
||||
} as any)
|
||||
} as any
|
||||
)
|
||||
|
||||
// Perform rename
|
||||
const newPath = 'workflows/dir/renamed.json'
|
||||
@@ -286,12 +287,12 @@ describe('useWorkflowStore', () => {
|
||||
expect(bookmarkStore.isBookmarked(workflow.path)).toBe(false)
|
||||
|
||||
// Mock super.rename
|
||||
jest
|
||||
.spyOn(Object.getPrototypeOf(workflow), 'rename')
|
||||
.mockImplementation(async function (this: any, newPath: string) {
|
||||
vi.spyOn(Object.getPrototypeOf(workflow), 'rename').mockImplementation(
|
||||
async function (this: any, newPath: string) {
|
||||
this.path = newPath
|
||||
return this
|
||||
} as any)
|
||||
} as any
|
||||
)
|
||||
|
||||
// Perform rename
|
||||
const newName = 'renamed'
|
||||
@@ -320,7 +321,7 @@ describe('useWorkflowStore', () => {
|
||||
const workflow = store.createTemporary('test.json')
|
||||
|
||||
// Mock the necessary methods
|
||||
jest.spyOn(workflow, 'delete').mockResolvedValue()
|
||||
vi.spyOn(workflow, 'delete').mockResolvedValue()
|
||||
|
||||
// Open the workflow first
|
||||
await store.openWorkflow(workflow)
|
||||
@@ -336,7 +337,7 @@ describe('useWorkflowStore', () => {
|
||||
const workflow = store.createTemporary('test.json')
|
||||
|
||||
// Mock delete method
|
||||
jest.spyOn(workflow, 'delete').mockResolvedValue()
|
||||
vi.spyOn(workflow, 'delete').mockResolvedValue()
|
||||
|
||||
// Bookmark the workflow
|
||||
bookmarkStore.setBookmarked(workflow.path, true)
|
||||
@@ -359,9 +360,9 @@ describe('useWorkflowStore', () => {
|
||||
const mockState = { nodes: [] }
|
||||
workflow.changeTracker = {
|
||||
activeState: mockState,
|
||||
reset: jest.fn()
|
||||
reset: vi.fn()
|
||||
} as any
|
||||
;(api.storeUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.storeUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
@@ -369,7 +370,7 @@ describe('useWorkflowStore', () => {
|
||||
modified: Date.now(),
|
||||
size: 2
|
||||
})
|
||||
})
|
||||
} as Response)
|
||||
|
||||
// Save the workflow
|
||||
await workflow.save()
|
||||
@@ -389,9 +390,9 @@ describe('useWorkflowStore', () => {
|
||||
const mockState = { nodes: [] }
|
||||
workflow.changeTracker = {
|
||||
activeState: mockState,
|
||||
reset: jest.fn()
|
||||
reset: vi.fn()
|
||||
} as any
|
||||
;(api.storeUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.storeUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
@@ -399,7 +400,7 @@ describe('useWorkflowStore', () => {
|
||||
modified: Date.now(),
|
||||
size: 2
|
||||
})
|
||||
})
|
||||
} as Response)
|
||||
|
||||
// Save the workflow
|
||||
await workflow.save()
|
||||
@@ -423,9 +424,9 @@ describe('useWorkflowStore', () => {
|
||||
const mockState = { nodes: [] }
|
||||
workflow.changeTracker = {
|
||||
activeState: mockState,
|
||||
reset: jest.fn()
|
||||
reset: vi.fn()
|
||||
} as any
|
||||
;(api.storeUserData as jest.Mock).mockResolvedValue({
|
||||
vi.mocked(api.storeUserData).mockResolvedValue({
|
||||
status: 200,
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
@@ -433,7 +434,7 @@ describe('useWorkflowStore', () => {
|
||||
modified: Date.now(),
|
||||
size: 2
|
||||
})
|
||||
})
|
||||
} as Response)
|
||||
|
||||
// Save the workflow with new path
|
||||
const newWorkflow = await workflow.saveAs('workflows/new-test.json')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { TreeNode } from 'primevue/treenode'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { buildTree, sortedTree } from '@/utils/treeUtil'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user