mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-28 08:17:36 +00:00
Compare commits
3 Commits
feat/cloud
...
test/stand
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35f79b7cd3 | ||
|
|
90a819bc87 | ||
|
|
69239ca2d5 |
105
src/utils/test-utils.test.ts
Normal file
105
src/utils/test-utils.test.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { render, screen, stubs } from '@/utils/test-utils'
|
||||
|
||||
import { defineComponent, h } from 'vue'
|
||||
|
||||
const TestButton = defineComponent({
|
||||
props: { label: { type: String, required: true } },
|
||||
setup(props) {
|
||||
return () => h('button', { 'data-testid': 'test-btn' }, props.label)
|
||||
}
|
||||
})
|
||||
|
||||
describe('test-utils', () => {
|
||||
it('renders a component with default plugins', () => {
|
||||
render(TestButton, { props: { label: 'Click me' } })
|
||||
expect(screen.getByTestId('test-btn')).toHaveTextContent('Click me')
|
||||
})
|
||||
|
||||
it('provides a userEvent instance by default', () => {
|
||||
const { user } = render(TestButton, { props: { label: 'Click' } })
|
||||
expect(user).toBeDefined()
|
||||
})
|
||||
|
||||
it('allows opting out of userEvent', () => {
|
||||
const { user } = render(TestButton, {
|
||||
props: { label: 'Click' },
|
||||
setupUser: false
|
||||
})
|
||||
expect(user).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('stubs', () => {
|
||||
describe('Button', () => {
|
||||
it('renders as a button element with label', () => {
|
||||
const Wrapper = defineComponent({
|
||||
components: { Button: stubs.Button },
|
||||
setup() {
|
||||
return () => h(stubs.Button, { label: 'Save' })
|
||||
}
|
||||
})
|
||||
render(Wrapper)
|
||||
expect(screen.getByRole('button')).toHaveTextContent('Save')
|
||||
})
|
||||
|
||||
it('sets disabled when loading is true', () => {
|
||||
const Wrapper = defineComponent({
|
||||
setup() {
|
||||
return () => h(stubs.Button, { label: 'Save', loading: true })
|
||||
}
|
||||
})
|
||||
render(Wrapper)
|
||||
expect(screen.getByRole('button')).toBeDisabled()
|
||||
})
|
||||
|
||||
it('emits click event', async () => {
|
||||
const onClick = vi.fn()
|
||||
const Wrapper = defineComponent({
|
||||
setup() {
|
||||
return () => h(stubs.Button, { label: 'Go', onClick })
|
||||
}
|
||||
})
|
||||
const { user } = render(Wrapper)
|
||||
await user!.click(screen.getByRole('button'))
|
||||
expect(onClick).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Skeleton', () => {
|
||||
it('renders with data-testid', () => {
|
||||
const Wrapper = defineComponent({
|
||||
setup() {
|
||||
return () => h(stubs.Skeleton)
|
||||
}
|
||||
})
|
||||
render(Wrapper)
|
||||
expect(screen.getByTestId('skeleton')).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Dialog', () => {
|
||||
it('renders children when visible', () => {
|
||||
const Wrapper = defineComponent({
|
||||
setup() {
|
||||
return () =>
|
||||
h(stubs.Dialog, { visible: true }, () => h('p', 'Dialog body'))
|
||||
}
|
||||
})
|
||||
render(Wrapper)
|
||||
expect(screen.getByRole('dialog')).toHaveTextContent('Dialog body')
|
||||
})
|
||||
|
||||
it('renders nothing when not visible', () => {
|
||||
const Wrapper = defineComponent({
|
||||
setup() {
|
||||
return () =>
|
||||
h(stubs.Dialog, { visible: false }, () => h('p', 'Hidden'))
|
||||
}
|
||||
})
|
||||
render(Wrapper)
|
||||
expect(screen.queryByRole('dialog')).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
170
src/utils/test-utils.ts
Normal file
170
src/utils/test-utils.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import type { RenderResult } from '@testing-library/vue'
|
||||
import type { ComponentMountingOptions } from '@vue/test-utils'
|
||||
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { render, screen } from '@testing-library/vue'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
import enMessages from '@/locales/en/main.json'
|
||||
|
||||
/**
|
||||
* Creates the default set of Vue plugins for component tests.
|
||||
*
|
||||
* - Pinia with `stubActions: false` (actions execute, but are spied)
|
||||
* - vue-i18n with English locale
|
||||
*
|
||||
* Pass additional plugins via the `plugins` option in `renderWithDefaults`.
|
||||
*/
|
||||
function createDefaultPlugins() {
|
||||
return [
|
||||
createTestingPinia({ stubActions: false }),
|
||||
createI18n({
|
||||
legacy: false,
|
||||
locale: 'en',
|
||||
messages: { en: enMessages }
|
||||
})
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Common directive stubs for components that use PrimeVue/custom directives.
|
||||
* Prevents "Failed to resolve directive" warnings in test output.
|
||||
*/
|
||||
const defaultDirectiveStubs: Record<string, () => void> = {
|
||||
tooltip: () => {}
|
||||
}
|
||||
|
||||
/**
|
||||
* PrimeVue component stubs for unit/component tests.
|
||||
*
|
||||
* Use via `global.stubs` in render options:
|
||||
* ```ts
|
||||
* render(MyComponent, { global: { stubs: { Button: stubs.Button } } })
|
||||
* ```
|
||||
*
|
||||
* Or use `renderWithDefaults` which auto-applies these as defaults.
|
||||
*/
|
||||
const ButtonStub = defineComponent({
|
||||
name: 'Button',
|
||||
props: ['disabled', 'loading', 'variant', 'size', 'label', 'icon', 'severity'],
|
||||
emits: ['click'],
|
||||
setup(props, { slots, emit }) {
|
||||
return () =>
|
||||
h(
|
||||
'button',
|
||||
{
|
||||
disabled: props.disabled || props.loading,
|
||||
'data-testid': props.label,
|
||||
'data-icon': props.icon,
|
||||
onClick: () => emit('click')
|
||||
},
|
||||
slots.default?.() ?? props.label
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const SkeletonStub = defineComponent({
|
||||
name: 'Skeleton',
|
||||
setup() {
|
||||
return () => h('div', { 'data-testid': 'skeleton' })
|
||||
}
|
||||
})
|
||||
|
||||
const TagStub = defineComponent({
|
||||
name: 'Tag',
|
||||
props: ['value', 'severity'],
|
||||
setup(props, { slots }) {
|
||||
return () => h('span', { 'data-testid': 'tag' }, slots.default?.() ?? props.value)
|
||||
}
|
||||
})
|
||||
|
||||
const BadgeStub = defineComponent({
|
||||
name: 'Badge',
|
||||
props: ['value', 'severity'],
|
||||
setup(props) {
|
||||
return () => h('span', { 'data-testid': 'badge' }, props.value)
|
||||
}
|
||||
})
|
||||
|
||||
const MessageStub = defineComponent({
|
||||
name: 'Message',
|
||||
props: ['severity', 'closable'],
|
||||
setup(_, { slots }) {
|
||||
return () => h('div', { 'data-testid': 'message', role: 'alert' }, slots.default?.())
|
||||
}
|
||||
})
|
||||
|
||||
const DialogStub = defineComponent({
|
||||
name: 'Dialog',
|
||||
props: ['visible', 'modal', 'header'],
|
||||
emits: ['update:visible'],
|
||||
setup(props, { slots }) {
|
||||
return () =>
|
||||
props.visible
|
||||
? h('div', { role: 'dialog', 'data-testid': 'dialog' }, [
|
||||
props.header ? h('div', props.header) : null,
|
||||
slots.default?.()
|
||||
])
|
||||
: null
|
||||
}
|
||||
})
|
||||
|
||||
const stubs = {
|
||||
Button: ButtonStub,
|
||||
Skeleton: SkeletonStub,
|
||||
Tag: TagStub,
|
||||
Badge: BadgeStub,
|
||||
Message: MessageStub,
|
||||
Dialog: DialogStub
|
||||
} as const
|
||||
|
||||
type RenderWithDefaultsResult = RenderResult & {
|
||||
user: ReturnType<typeof userEvent.setup> | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a Vue component with standard test infrastructure pre-configured:
|
||||
* - Pinia testing store (actions execute but are spied)
|
||||
* - vue-i18n with English messages
|
||||
* - Common directive stubs (tooltip)
|
||||
* - Optional userEvent instance
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { render, screen } from '@/utils/test-utils'
|
||||
*
|
||||
* it('renders button text', async () => {
|
||||
* const { user } = render(MyComponent, { props: { label: 'Click' } })
|
||||
* expect(screen.getByRole('button')).toHaveTextContent('Click')
|
||||
* await user!.click(screen.getByRole('button'))
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
function renderWithDefaults<C>(
|
||||
component: C,
|
||||
options?: ComponentMountingOptions<C> & { setupUser?: boolean }
|
||||
): RenderWithDefaultsResult {
|
||||
const { setupUser = true, global: globalOptions, ...rest } = options ?? {}
|
||||
const user = setupUser ? userEvent.setup() : undefined
|
||||
|
||||
const result = render(
|
||||
component as Parameters<typeof render>[0],
|
||||
{
|
||||
global: {
|
||||
...globalOptions,
|
||||
plugins: [...createDefaultPlugins(), ...(globalOptions?.plugins ?? [])],
|
||||
directives: {
|
||||
...defaultDirectiveStubs,
|
||||
...globalOptions?.directives
|
||||
}
|
||||
},
|
||||
...rest
|
||||
} as Parameters<typeof render>[1]
|
||||
)
|
||||
|
||||
return { ...result, user }
|
||||
}
|
||||
|
||||
export { renderWithDefaults as render, screen, stubs }
|
||||
Reference in New Issue
Block a user