refactor: migrate high-risk overlay follow-up selectors (#33795)

Signed-off-by: yyh <yuanyouhuilyz@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
yyh
2026-03-20 14:12:35 +08:00
committed by GitHub
parent d6e247849f
commit 978ebbf9ea
17 changed files with 478 additions and 424 deletions

View File

@ -13,12 +13,20 @@ vi.mock('@/app/components/base/amplitude', () => ({
trackEvent: vi.fn(),
}))
vi.mock('@/app/components/base/toast', () => ({
default: {
notify: vi.fn(),
},
const { mockToastNotify } = vi.hoisted(() => ({
mockToastNotify: vi.fn(),
}))
vi.mock('@/app/components/base/toast', async (importOriginal) => {
const actual = await importOriginal<typeof import('@/app/components/base/toast')>()
return {
...actual,
default: Object.assign(actual.default, {
notify: mockToastNotify,
}),
}
})
const mockCreateEmptyDataset = vi.fn()
const mockInvalidDatasetList = vi.fn()
@ -37,6 +45,8 @@ vi.mock('@/service/knowledge/use-dataset', () => ({
describe('CreateCard', () => {
beforeEach(() => {
vi.clearAllMocks()
mockToastNotify.mockReset()
mockToastNotify.mockImplementation(() => ({ clear: vi.fn() }))
})
describe('Rendering', () => {

View File

@ -1,8 +1,6 @@
import type { PipelineTemplate } from '@/models/pipeline'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import Toast from '@/app/components/base/toast'
import { ChunkingMode } from '@/models/datasets'
import EditPipelineInfo from '../edit-pipeline-info'
@ -16,12 +14,21 @@ vi.mock('@/service/use-pipeline', () => ({
useInvalidCustomizedTemplateList: () => mockInvalidCustomizedTemplateList,
}))
vi.mock('@/app/components/base/toast', () => ({
default: {
notify: vi.fn(),
},
const { mockToastAdd } = vi.hoisted(() => ({
mockToastAdd: 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,
add: mockToastAdd,
},
}
})
// Mock AppIconPicker to capture interactions
let _mockOnSelect: ((icon: { type: 'emoji' | 'image', icon?: string, background?: string, fileId?: string, url?: string }) => void) | undefined
let _mockOnClose: (() => void) | undefined
@ -88,6 +95,7 @@ describe('EditPipelineInfo', () => {
beforeEach(() => {
vi.clearAllMocks()
mockToastAdd.mockReset()
_mockOnSelect = undefined
_mockOnClose = undefined
})
@ -235,9 +243,9 @@ describe('EditPipelineInfo', () => {
fireEvent.click(saveButton)
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: 'Please enter a name for the Knowledge Base.',
title: 'datasetPipeline.editPipelineInfoNameRequired',
})
})
})

View File

@ -1,7 +1,6 @@
import type { PipelineTemplate } from '@/models/pipeline'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import Toast from '@/app/components/base/toast'
import { ChunkingMode } from '@/models/datasets'
import TemplateCard from '../index'
@ -15,12 +14,21 @@ vi.mock('@/app/components/base/amplitude', () => ({
trackEvent: vi.fn(),
}))
vi.mock('@/app/components/base/toast', () => ({
default: {
notify: vi.fn(),
},
const { mockToastAdd } = vi.hoisted(() => ({
mockToastAdd: 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,
add: mockToastAdd,
},
}
})
// Mock download utilities
vi.mock('@/utils/download', () => ({
downloadBlob: vi.fn(),
@ -174,6 +182,7 @@ describe('TemplateCard', () => {
beforeEach(() => {
vi.clearAllMocks()
mockToastAdd.mockReset()
mockIsExporting = false
_capturedOnConfirm = undefined
_capturedOnCancel = undefined
@ -228,9 +237,9 @@ describe('TemplateCard', () => {
fireEvent.click(chooseButton)
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
title: expect.any(String),
})
})
})
@ -291,9 +300,9 @@ describe('TemplateCard', () => {
fireEvent.click(chooseButton)
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'success',
message: expect.any(String),
title: expect.any(String),
})
})
})
@ -309,9 +318,9 @@ describe('TemplateCard', () => {
fireEvent.click(chooseButton)
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
title: expect.any(String),
})
})
})
@ -458,9 +467,9 @@ describe('TemplateCard', () => {
fireEvent.click(exportButton)
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'success',
message: expect.any(String),
title: expect.any(String),
})
})
})
@ -476,9 +485,9 @@ describe('TemplateCard', () => {
fireEvent.click(exportButton)
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
title: expect.any(String),
})
})
})

View File

@ -32,16 +32,21 @@ vi.mock('@/service/base', () => ({
ssePost: mockSsePost,
}))
// Mock Toast.notify - static method that manipulates DOM, needs mocking to verify calls
const { mockToastNotify } = vi.hoisted(() => ({
mockToastNotify: vi.fn(),
// Mock toast.add because the component reports errors through the UI toast manager.
const { mockToastAdd } = vi.hoisted(() => ({
mockToastAdd: vi.fn(),
}))
vi.mock('@/app/components/base/toast', () => ({
default: {
notify: mockToastNotify,
},
}))
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,
add: mockToastAdd,
},
}
})
// Mock useGetDataSourceAuth - API service hook requires mocking
const { mockUseGetDataSourceAuth } = vi.hoisted(() => ({
@ -192,6 +197,7 @@ const createDefaultProps = (overrides?: Partial<OnlineDocumentsProps>): OnlineDo
describe('OnlineDocuments', () => {
beforeEach(() => {
vi.clearAllMocks()
mockToastAdd.mockReset()
// Reset store state
mockStoreState.documentsData = []
@ -509,9 +515,9 @@ describe('OnlineDocuments', () => {
render(<OnlineDocuments {...props} />)
await waitFor(() => {
expect(mockToastNotify).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: 'Something went wrong',
title: 'Something went wrong',
})
})
})
@ -774,9 +780,9 @@ describe('OnlineDocuments', () => {
render(<OnlineDocuments {...props} />)
await waitFor(() => {
expect(mockToastNotify).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: 'API Error Message',
title: 'API Error Message',
})
})
})
@ -1094,9 +1100,9 @@ describe('OnlineDocuments', () => {
render(<OnlineDocuments {...props} />)
await waitFor(() => {
expect(mockToastNotify).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: 'Failed to fetch documents',
title: 'Failed to fetch documents',
})
})

View File

@ -45,15 +45,20 @@ vi.mock('@/service/use-datasource', () => ({
useGetDataSourceAuth: mockUseGetDataSourceAuth,
}))
const { mockToastNotify } = vi.hoisted(() => ({
mockToastNotify: vi.fn(),
const { mockToastAdd } = vi.hoisted(() => ({
mockToastAdd: vi.fn(),
}))
vi.mock('@/app/components/base/toast', () => ({
default: {
notify: mockToastNotify,
},
}))
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,
add: mockToastAdd,
},
}
})
// Note: zustand/react/shallow useShallow is imported directly (simple utility function)
@ -231,6 +236,7 @@ const resetMockStoreState = () => {
describe('OnlineDrive', () => {
beforeEach(() => {
vi.clearAllMocks()
mockToastAdd.mockReset()
// Reset store state
resetMockStoreState()
@ -541,9 +547,9 @@ describe('OnlineDrive', () => {
render(<OnlineDrive {...props} />)
await waitFor(() => {
expect(mockToastNotify).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: errorMessage,
title: errorMessage,
})
})
})
@ -915,9 +921,9 @@ describe('OnlineDrive', () => {
render(<OnlineDrive {...props} />)
await waitFor(() => {
expect(mockToastNotify).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: errorMessage,
title: errorMessage,
})
})
})

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 { mockToastAdd } = vi.hoisted(() => ({
mockToastAdd: 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,
add: mockToastAdd,
},
}
})
// 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() }))
mockToastAdd.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,7 +643,7 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
// Assert - Toast should be called with error message
expect(toastNotifySpy).toHaveBeenCalledWith(
expect(mockToastAdd).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
}),
@ -660,10 +665,10 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
// Assert - Toast message should contain field path
expect(toastNotifySpy).toHaveBeenCalledWith(
expect(mockToastAdd).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
message: expect.stringContaining('email_address'),
title: expect.stringContaining('email_address'),
}),
)
})
@ -714,8 +719,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(mockToastAdd).toHaveBeenCalledTimes(1)
expect(mockToastAdd).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
}),
@ -738,7 +743,7 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
// Assert - No toast error, onSubmit called
expect(toastNotifySpy).not.toHaveBeenCalled()
expect(mockToastAdd).not.toHaveBeenCalled()
expect(mockOnSubmit).toHaveBeenCalled()
})
@ -835,7 +840,7 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
expect(mockOnSubmit).toHaveBeenCalled()
expect(toastNotifySpy).not.toHaveBeenCalled()
expect(mockToastAdd).not.toHaveBeenCalled()
})
it('should fail validation with invalid data', () => {
@ -854,7 +859,7 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
expect(mockOnSubmit).not.toHaveBeenCalled()
expect(toastNotifySpy).toHaveBeenCalled()
expect(mockToastAdd).toHaveBeenCalled()
})
it('should show error toast message when validation fails', () => {
@ -871,10 +876,10 @@ describe('Options', () => {
fireEvent.click(screen.getByRole('button'))
expect(toastNotifySpy).toHaveBeenCalledWith(
expect(mockToastAdd).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
message: expect.any(String),
title: expect.any(String),
}),
)
})

View File

@ -1,13 +1,24 @@
import type { NotionPage } from '@/models/common'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
import Toast from '@/app/components/base/toast'
import OnlineDocumentPreview from '../online-document-preview'
// Uses global react-i18next mock from web/vitest.setup.ts
// Spy on Toast.notify
const toastNotifySpy = vi.spyOn(Toast, 'notify')
const { mockToastAdd } = vi.hoisted(() => ({
mockToastAdd: 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,
add: mockToastAdd,
},
}
})
// Mock dataset-detail context - needs mock to control return values
const mockPipelineId = vi.fn()
@ -56,6 +67,7 @@ const defaultProps = {
describe('OnlineDocumentPreview', () => {
beforeEach(() => {
vi.clearAllMocks()
mockToastAdd.mockReset()
mockPipelineId.mockReturnValue('pipeline-123')
mockUsePreviewOnlineDocument.mockReturnValue({
mutateAsync: mockMutateAsync,
@ -258,9 +270,9 @@ describe('OnlineDocumentPreview', () => {
render(<OnlineDocumentPreview {...defaultProps} />)
await waitFor(() => {
expect(toastNotifySpy).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: errorMessage,
title: errorMessage,
})
})
})
@ -276,9 +288,9 @@ describe('OnlineDocumentPreview', () => {
render(<OnlineDocumentPreview {...defaultProps} />)
await waitFor(() => {
expect(toastNotifySpy).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: 'Network Error',
title: 'Network Error',
})
})
})

View File

@ -3,13 +3,24 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
import * as z from 'zod'
import { BaseFieldType } from '@/app/components/base/form/form-scenarios/base/types'
import Toast from '@/app/components/base/toast'
import Actions from '../actions'
import Form from '../form'
import Header from '../header'
// Spy on Toast.notify for validation tests
const toastNotifySpy = vi.spyOn(Toast, 'notify')
const { mockToastAdd } = vi.hoisted(() => ({
mockToastAdd: 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,
add: mockToastAdd,
},
}
})
// Test Data Factory Functions
@ -335,7 +346,7 @@ describe('Form', () => {
beforeEach(() => {
vi.clearAllMocks()
toastNotifySpy.mockClear()
mockToastAdd.mockReset()
})
describe('Rendering', () => {
@ -444,9 +455,9 @@ describe('Form', () => {
// Assert - validation error should be shown
await waitFor(() => {
expect(toastNotifySpy).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: '"field1" is required',
title: '"field1" is required',
})
})
})
@ -566,9 +577,9 @@ describe('Form', () => {
fireEvent.submit(form)
await waitFor(() => {
expect(toastNotifySpy).toHaveBeenCalledWith({
expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
message: '"field1" is required',
title: '"field1" is required',
})
})
})
@ -583,7 +594,7 @@ describe('Form', () => {
// Assert - wait a bit and verify onSubmit was not called
await waitFor(() => {
expect(toastNotifySpy).toHaveBeenCalled()
expect(mockToastAdd).toHaveBeenCalled()
})
expect(onSubmit).not.toHaveBeenCalled()
})

View File

@ -2,10 +2,23 @@ import type { BaseConfiguration } from '@/app/components/base/form/form-scenario
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { z } from 'zod'
import Toast from '@/app/components/base/toast'
import Form from '../form'
const { mockToastAdd } = vi.hoisted(() => ({
mockToastAdd: 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,
add: mockToastAdd,
},
}
})
// Mock the Header component (sibling component, not a base component)
vi.mock('../header', () => ({
default: ({ onReset, resetDisabled, onPreview, previewDisabled }: {
@ -44,7 +57,7 @@ const defaultProps = {
describe('Form (process-documents)', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.spyOn(Toast, 'notify').mockImplementation(() => ({ clear: vi.fn() }))
mockToastAdd.mockReset()
})
// Verify basic rendering of form structure
@ -106,8 +119,11 @@ describe('Form (process-documents)', () => {
fireEvent.submit(form)
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'error' }),
expect(mockToastAdd).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
title: '"name" Name is required',
}),
)
})
})
@ -121,7 +137,7 @@ describe('Form (process-documents)', () => {
await waitFor(() => {
expect(defaultProps.onSubmit).toHaveBeenCalled()
})
expect(Toast.notify).not.toHaveBeenCalled()
expect(mockToastAdd).not.toHaveBeenCalled()
})
})