Merge main HEAD (segment 5) into sandboxed-agent-rebase

Resolve 83 conflicts: 10 backend, 62 frontend, 11 config/lock files.
Preserve sandbox/agent/collaboration features while adopting main's
UI refactorings (Dialog/AlertDialog/Popover), model provider updates,
and enterprise features.

Made-with: Cursor
This commit is contained in:
Novice
2026-03-23 14:20:06 +08:00
1671 changed files with 124822 additions and 22302 deletions

View File

@ -1,13 +1,26 @@
import type { MockInstance } from 'vitest'
import type { RAGPipelineVariables } from '@/models/pipeline'
import { fireEvent, render, screen } from '@testing-library/react'
import * as React from 'react'
import { BaseFieldType } from '@/app/components/base/form/form-scenarios/base/types'
import Toast from '@/app/components/base/toast'
import { CrawlStep } from '@/models/datasets'
import { PipelineInputVarType } from '@/models/pipeline'
import Options from '../index'
const { mockToastError } = vi.hoisted(() => ({
mockToastError: vi.fn(),
}))
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
const actual = await importOriginal<typeof import('@/app/components/base/ui/toast')>()
return {
...actual,
toast: {
...actual.toast,
error: mockToastError,
},
}
})
// Mock useInitialData and useConfigurations hooks
const { mockUseInitialData, mockUseConfigurations } = vi.hoisted(() => ({
mockUseInitialData: vi.fn(),
@ -116,13 +129,9 @@ const createDefaultProps = (overrides?: Partial<OptionsProps>): OptionsProps =>
})
describe('Options', () => {
let toastNotifySpy: MockInstance
beforeEach(() => {
vi.clearAllMocks()
// Spy on Toast.notify instead of mocking the entire module
toastNotifySpy = vi.spyOn(Toast, 'notify').mockImplementation(() => ({ clear: vi.fn() }))
mockToastError.mockReset()
// Reset mock form values
Object.keys(mockFormValues).forEach(key => delete mockFormValues[key])
@ -132,10 +141,6 @@ describe('Options', () => {
mockUseConfigurations.mockReturnValue([createMockConfiguration()])
})
afterEach(() => {
toastNotifySpy.mockRestore()
})
describe('Rendering', () => {
it('should render without crashing', () => {
const props = createDefaultProps()
@ -638,11 +643,7 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
// Assert - Toast should be called with error message
expect(toastNotifySpy).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
}),
)
expect(mockToastError).toHaveBeenCalled()
})
it('should handle validation error and display field name in message', () => {
@ -660,12 +661,7 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
// Assert - Toast message should contain field path
expect(toastNotifySpy).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
message: expect.stringContaining('email_address'),
}),
)
expect(mockToastError).toHaveBeenCalledWith(expect.stringContaining('email_address'))
})
it('should handle empty variables gracefully', () => {
@ -714,12 +710,8 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
// Assert - Toast should be called once (only first error)
expect(toastNotifySpy).toHaveBeenCalledTimes(1)
expect(toastNotifySpy).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
}),
)
expect(mockToastError).toHaveBeenCalledTimes(1)
expect(mockToastError).toHaveBeenCalled()
})
it('should handle validation pass when all required fields have values', () => {
@ -738,7 +730,7 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
// Assert - No toast error, onSubmit called
expect(toastNotifySpy).not.toHaveBeenCalled()
expect(mockToastError).not.toHaveBeenCalled()
expect(mockOnSubmit).toHaveBeenCalled()
})
@ -835,7 +827,7 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
expect(mockOnSubmit).toHaveBeenCalled()
expect(toastNotifySpy).not.toHaveBeenCalled()
expect(mockToastError).not.toHaveBeenCalled()
})
it('should fail validation with invalid data', () => {
@ -854,7 +846,7 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
expect(mockOnSubmit).not.toHaveBeenCalled()
expect(toastNotifySpy).toHaveBeenCalled()
expect(mockToastError).toHaveBeenCalled()
})
it('should show error toast message when validation fails', () => {
@ -871,12 +863,7 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
expect(toastNotifySpy).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
message: expect.any(String),
}),
)
expect(mockToastError).toHaveBeenCalledWith(expect.any(String))
})
})

View File

@ -8,7 +8,7 @@ import { useAppForm } from '@/app/components/base/form'
import BaseField from '@/app/components/base/form/form-scenarios/base/field'
import { generateZodSchema } from '@/app/components/base/form/form-scenarios/base/utils'
import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/general'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import { useConfigurations, useInitialData } from '@/app/components/rag-pipeline/hooks/use-input-fields'
import { CrawlStep } from '@/models/datasets'
import { cn } from '@/utils/classnames'
@ -44,10 +44,7 @@ const Options = ({
const issues = result.error.issues
const firstIssue = issues[0]
const errorMessage = `"${firstIssue.path.join('.')}" ${firstIssue.message}`
Toast.notify({
type: 'error',
message: errorMessage,
})
toast.error(errorMessage)
return errorMessage
}
return undefined