Compare commits

...

3 Commits

Author SHA1 Message Date
Stephen Zhou
9d1d76055a test 2026-01-30 16:29:46 +08:00
Stephen Zhou
a34d3ffc28 update 2026-01-30 16:26:20 +08:00
Stephen Zhou
1d2ed8da2a test: fix failed test 2026-01-30 16:16:50 +08:00
5 changed files with 36 additions and 27 deletions

View File

@@ -124,7 +124,7 @@ describe('CreateAppModal', () => {
const nameInput = screen.getByPlaceholderText('app.newApp.appNamePlaceholder')
fireEvent.change(nameInput, { target: { value: 'My App' } })
fireEvent.click(screen.getByRole('button', { name: 'app.newApp.Create' }))
fireEvent.click(screen.getByRole('button', { name: /app\.newApp\.Create/ }))
await waitFor(() => expect(mockCreateApp).toHaveBeenCalledWith({
name: 'My App',
@@ -152,7 +152,7 @@ describe('CreateAppModal', () => {
const nameInput = screen.getByPlaceholderText('app.newApp.appNamePlaceholder')
fireEvent.change(nameInput, { target: { value: 'My App' } })
fireEvent.click(screen.getByRole('button', { name: 'app.newApp.Create' }))
fireEvent.click(screen.getByRole('button', { name: /app\.newApp\.Create/ }))
await waitFor(() => expect(mockCreateApp).toHaveBeenCalled())
expect(mockNotify).toHaveBeenCalledWith({ type: 'error', message: 'boom' })

View File

@@ -138,7 +138,7 @@ describe('CreateAppModal', () => {
setup({ appName: 'My App', isEditModal: false })
expect(screen.getByText('explore.appCustomize.title:{"name":"My App"}')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.create' })).toBeInTheDocument()
expect(screen.getByRole('button', { name: /common\.operation\.create/ })).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.cancel' })).toBeInTheDocument()
})
@@ -146,7 +146,7 @@ describe('CreateAppModal', () => {
setup({ isEditModal: true, appMode: AppModeEnum.CHAT, max_active_requests: 5 })
expect(screen.getByText('app.editAppTitle')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.save' })).toBeInTheDocument()
expect(screen.getByRole('button', { name: /common\.operation\.save/ })).toBeInTheDocument()
expect(screen.getByRole('switch')).toBeInTheDocument()
expect((screen.getByRole('spinbutton') as HTMLInputElement).value).toBe('5')
})
@@ -166,7 +166,7 @@ describe('CreateAppModal', () => {
it('should not render modal content when hidden', () => {
setup({ show: false })
expect(screen.queryByRole('button', { name: 'common.operation.create' })).not.toBeInTheDocument()
expect(screen.queryByRole('button', { name: /common\.operation\.create/ })).not.toBeInTheDocument()
})
})
@@ -175,13 +175,13 @@ describe('CreateAppModal', () => {
it('should disable confirm action when confirmDisabled is true', () => {
setup({ confirmDisabled: true })
expect(screen.getByRole('button', { name: 'common.operation.create' })).toBeDisabled()
expect(screen.getByRole('button', { name: /common\.operation\.create/ })).toBeDisabled()
})
it('should disable confirm action when appName is empty', () => {
setup({ appName: ' ' })
expect(screen.getByRole('button', { name: 'common.operation.create' })).toBeDisabled()
expect(screen.getByRole('button', { name: /common\.operation\.create/ })).toBeDisabled()
})
})
@@ -245,7 +245,7 @@ describe('CreateAppModal', () => {
setup({ isEditModal: false })
expect(screen.getByText('billing.apps.fullTip2')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.create' })).toBeDisabled()
expect(screen.getByRole('button', { name: /common\.operation\.create/ })).toBeDisabled()
})
it('should allow saving when apps quota is reached in edit mode', () => {
@@ -257,7 +257,7 @@ describe('CreateAppModal', () => {
setup({ isEditModal: true })
expect(screen.queryByText('billing.apps.fullTip2')).not.toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.save' })).toBeEnabled()
expect(screen.getByRole('button', { name: /common\.operation\.save/ })).toBeEnabled()
})
})
@@ -384,7 +384,7 @@ describe('CreateAppModal', () => {
fireEvent.click(screen.getByRole('button', { name: 'app.iconPicker.ok' }))
fireEvent.click(screen.getByRole('button', { name: 'common.operation.create' }))
fireEvent.click(screen.getByRole('button', { name: /common\.operation\.create/ }))
act(() => {
vi.advanceTimersByTime(300)
})
@@ -433,7 +433,7 @@ describe('CreateAppModal', () => {
expect(screen.queryByRole('button', { name: 'app.iconPicker.cancel' })).not.toBeInTheDocument()
// Submit and verify the payload uses the original icon (cancel reverts to props)
fireEvent.click(screen.getByRole('button', { name: 'common.operation.create' }))
fireEvent.click(screen.getByRole('button', { name: /common\.operation\.create/ }))
act(() => {
vi.advanceTimersByTime(300)
})
@@ -471,7 +471,7 @@ describe('CreateAppModal', () => {
appIconBackground: '#000000',
})
fireEvent.click(screen.getByRole('button', { name: 'common.operation.create' }))
fireEvent.click(screen.getByRole('button', { name: /common\.operation\.create/ }))
act(() => {
vi.advanceTimersByTime(300)
})
@@ -495,7 +495,7 @@ describe('CreateAppModal', () => {
const { onConfirm } = setup({ appDescription: 'Old description' })
fireEvent.change(screen.getByPlaceholderText('app.newApp.appDescriptionPlaceholder'), { target: { value: 'Updated description' } })
fireEvent.click(screen.getByRole('button', { name: 'common.operation.create' }))
fireEvent.click(screen.getByRole('button', { name: /common\.operation\.create/ }))
act(() => {
vi.advanceTimersByTime(300)
})
@@ -512,7 +512,7 @@ describe('CreateAppModal', () => {
appIconBackground: null,
})
fireEvent.click(screen.getByRole('button', { name: 'common.operation.create' }))
fireEvent.click(screen.getByRole('button', { name: /common\.operation\.create/ }))
act(() => {
vi.advanceTimersByTime(300)
})
@@ -536,7 +536,7 @@ describe('CreateAppModal', () => {
fireEvent.click(screen.getByRole('switch'))
fireEvent.change(screen.getByRole('spinbutton'), { target: { value: '12' } })
fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
fireEvent.click(screen.getByRole('button', { name: /common\.operation\.save/ }))
act(() => {
vi.advanceTimersByTime(300)
})
@@ -551,7 +551,7 @@ describe('CreateAppModal', () => {
it('should omit max_active_requests when input is empty', () => {
const { onConfirm } = setup({ isEditModal: true, max_active_requests: null })
fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
fireEvent.click(screen.getByRole('button', { name: /common\.operation\.save/ }))
act(() => {
vi.advanceTimersByTime(300)
})
@@ -564,7 +564,7 @@ describe('CreateAppModal', () => {
const { onConfirm } = setup({ isEditModal: true, max_active_requests: null })
fireEvent.change(screen.getByRole('spinbutton'), { target: { value: 'abc' } })
fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
fireEvent.click(screen.getByRole('button', { name: /common\.operation\.save/ }))
act(() => {
vi.advanceTimersByTime(300)
})
@@ -576,7 +576,7 @@ describe('CreateAppModal', () => {
it('should show toast error and not submit when name becomes empty before debounced submit runs', () => {
const { onConfirm, onHide } = setup({ appName: 'My App' })
fireEvent.click(screen.getByRole('button', { name: 'common.operation.create' }))
fireEvent.click(screen.getByRole('button', { name: /common\.operation\.create/ }))
fireEvent.change(screen.getByPlaceholderText('app.newApp.appNamePlaceholder'), { target: { value: ' ' } })
act(() => {

View File

@@ -86,6 +86,7 @@ vi.mock('./actions/commands/registry', () => ({
vi.mock('@/app/components/workflow/utils/common', () => ({
getKeyboardKeyCodeBySystem: () => 'ctrl',
getKeyboardKeyNameBySystem: (key: string) => key,
isEventTargetInputArea: () => false,
isMac: () => false,
}))

View File

@@ -134,19 +134,20 @@ vi.mock('@/app/components/workflow/constants', () => ({
WORKFLOW_DATA_UPDATE: 'WORKFLOW_DATA_UPDATE',
}))
// Mock FileReader
// Mock FileReader - synchronous to avoid timing issues in tests
class MockFileReader {
result: string | null = null
onload: ((e: { target: { result: string | null } }) => void) | null = null
readAsText(_file: File) {
// Simulate async file reading
setTimeout(() => {
this.result = 'test file content'
if (this.onload) {
// Call onload synchronously to avoid race conditions in tests
this.result = 'test file content'
// Use queueMicrotask instead of setTimeout to ensure it runs before other timers
// but still allows React state updates to complete
queueMicrotask(() => {
if (this.onload)
this.onload({ target: { result: this.result } })
}
}, 0)
})
}
}

View File

@@ -33,6 +33,11 @@ vi.mock('@/context/i18n', () => ({
useDocLink: () => (path: string) => `https://docs.example.com${path}`,
}))
// Mock workflow utils for ShortcutsName component
vi.mock('@/app/components/workflow/utils', () => ({
getKeyboardKeyNameBySystem: (key: string) => key,
}))
// Mock StartNodeSelectionPanel (using real component would be better for integration,
// but for this test we'll mock to control behavior)
vi.mock('./start-node-selection-panel', () => ({
@@ -551,8 +556,10 @@ describe('WorkflowOnboardingModal', () => {
// Assert
const escKey = screen.getByText('workflow.onboarding.escTip.key')
expect(escKey.closest('kbd')).toBeInTheDocument()
expect(escKey.closest('kbd')).toHaveClass('system-kbd')
// ShortcutsName renders a div with system-kbd class, not a kbd element
const kbdContainer = escKey.closest('.system-kbd')
expect(kbdContainer).toBeInTheDocument()
expect(kbdContainer).toHaveClass('system-kbd')
})
it('should have descriptive text for ESC functionality', () => {