chore: enable noUncheckedIndexedAccess (#35178)

This commit is contained in:
Stephen Zhou 2026-04-16 21:01:35 +08:00 committed by GitHub
parent e507675860
commit abb84f1c38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
729 changed files with 14660 additions and 7803 deletions

View File

@ -0,0 +1,28 @@
#!/usr/bin/env node
import { spawnSync } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const entryFile = path.join(packageRoot, 'dist', 'cli.mjs')
if (!fs.existsSync(entryFile))
throw new Error(`Built CLI entry not found at ${entryFile}. Run "pnpm --filter migrate-no-unchecked-indexed-access build" first.`)
const result = spawnSync(
process.execPath,
[entryFile, ...process.argv.slice(2)],
{
cwd: process.cwd(),
env: process.env,
stdio: 'inherit',
},
)
if (result.error)
throw result.error
process.exit(result.status ?? 1)

View File

@ -0,0 +1,20 @@
{
"name": "migrate-no-unchecked-indexed-access",
"private": true,
"version": "0.0.0-private",
"type": "module",
"bin": {
"migrate-no-unchecked-indexed-access": "./bin/migrate-no-unchecked-indexed-access.js"
},
"scripts": {
"build": "vp pack"
},
"dependencies": {
"typescript": "catalog:"
},
"devDependencies": {
"@types/node": "catalog:",
"vite": "catalog:",
"vite-plus": "catalog:"
}
}

View File

@ -0,0 +1,45 @@
import process from 'node:process'
import { runBatchMigrationCommand } from './no-unchecked-indexed-access/run'
function printUsage() {
console.log(`Usage:
migrate-no-unchecked-indexed-access [options]
Options:
--project <path>
--batch-size <number>
--batch-iterations <number>
--max-rounds <number>
--verbose`)
}
async function flushStandardStreams() {
await Promise.all([
new Promise<void>(resolve => process.stdout.write('', () => resolve())),
new Promise<void>(resolve => process.stderr.write('', () => resolve())),
])
}
async function main() {
const argv = process.argv.slice(2)
if (argv.includes('help') || argv.includes('--help') || argv.includes('-h')) {
printUsage()
return
}
await runBatchMigrationCommand(argv)
}
let exitCode = 0
try {
await main()
exitCode = process.exitCode ?? 0
}
catch (error) {
console.error(error instanceof Error ? error.message : error)
exitCode = 1
}
await flushStandardStreams()
process.exit(exitCode)

View File

@ -0,0 +1,51 @@
import fs from 'node:fs/promises'
import path from 'node:path'
import process from 'node:process'
import { normalizeMalformedAssertions } from './migrate'
const ROOT = process.cwd()
const EXTENSIONS = new Set(['.ts', '.tsx'])
async function collectFiles(directory: string): Promise<string[]> {
const entries = await fs.readdir(directory, { withFileTypes: true })
const files: string[] = []
for (const entry of entries) {
if (entry.name === 'node_modules' || entry.name === '.next')
continue
const absolutePath = path.join(directory, entry.name)
if (entry.isDirectory()) {
files.push(...await collectFiles(absolutePath))
continue
}
if (!EXTENSIONS.has(path.extname(entry.name)))
continue
files.push(absolutePath)
}
return files
}
async function main() {
const files = await collectFiles(ROOT)
let changedFileCount = 0
await Promise.all(files.map(async (fileName) => {
const currentText = await fs.readFile(fileName, 'utf8')
const nextText = normalizeMalformedAssertions(currentText)
if (nextText === currentText)
return
await fs.writeFile(fileName, nextText)
changedFileCount += 1
}))
console.log(`Normalized malformed assertion syntax in ${changedFileCount} file(s).`)
}
export async function runNormalizeCommand(_argv: string[]) {
await main()
}

View File

@ -0,0 +1,325 @@
import { execFile } from 'node:child_process'
import { createHash } from 'node:crypto'
import fs from 'node:fs/promises'
import os from 'node:os'
import path from 'node:path'
import process from 'node:process'
import { promisify } from 'node:util'
import { runMigration, SUPPORTED_DIAGNOSTIC_CODES } from './migrate'
const execFileAsync = promisify(execFile)
const DIAGNOSTIC_PATTERN = /^(.+?\.(?:ts|tsx))\((\d+),(\d+)\): error TS(\d+): (.+)$/
const DEFAULT_BATCH_SIZE = 100
const DEFAULT_BATCH_ITERATIONS = 5
const DEFAULT_MAX_ROUNDS = 20
const TYPECHECK_CACHE_DIR = path.join(os.tmpdir(), 'migrate-no-unchecked-indexed-access')
type CliOptions = {
batchIterations: number
batchSize: number
maxRounds: number
project: string
verbose: boolean
}
type DiagnosticEntry = {
code: number
fileName: string
line: number
message: string
}
function parseArgs(argv: string[]): CliOptions {
const options: CliOptions = {
batchIterations: DEFAULT_BATCH_ITERATIONS,
batchSize: DEFAULT_BATCH_SIZE,
maxRounds: DEFAULT_MAX_ROUNDS,
project: 'tsconfig.json',
verbose: false,
}
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i]
if (arg === '--')
continue
if (arg === '--verbose') {
options.verbose = true
continue
}
if (arg === '--project') {
const value = argv[i + 1]
if (!value)
throw new Error('Missing value for --project')
options.project = value
i += 1
continue
}
if (arg === '--batch-size') {
const value = Number(argv[i + 1])
if (!Number.isInteger(value) || value <= 0)
throw new Error('Invalid value for --batch-size')
options.batchSize = value
i += 1
continue
}
if (arg === '--batch-iterations') {
const value = Number(argv[i + 1])
if (!Number.isInteger(value) || value <= 0)
throw new Error('Invalid value for --batch-iterations')
options.batchIterations = value
i += 1
continue
}
if (arg === '--max-rounds') {
const value = Number(argv[i + 1])
if (!Number.isInteger(value) || value <= 0)
throw new Error('Invalid value for --max-rounds')
options.maxRounds = value
i += 1
continue
}
throw new Error(`Unknown option: ${arg}`)
}
return options
}
function getTypeCheckBuildInfoPath(projectPath: string): string {
const hash = createHash('sha1')
.update(projectPath)
.digest('hex')
.slice(0, 16)
return path.join(TYPECHECK_CACHE_DIR, `${hash}.tsbuildinfo`)
}
async function runTypeCheck(
project: string,
options?: {
incremental?: boolean
},
): Promise<{ diagnostics: DiagnosticEntry[], exitCode: number, rawOutput: string }> {
const projectPath = path.resolve(process.cwd(), project)
const projectDirectory = path.dirname(projectPath)
const buildInfoPath = getTypeCheckBuildInfoPath(projectPath)
const incremental = options?.incremental ?? true
await fs.mkdir(TYPECHECK_CACHE_DIR, { recursive: true })
const tscArgs = ['exec', 'tsc', '--noEmit', '--pretty', 'false']
if (incremental) {
tscArgs.push('--incremental', '--tsBuildInfoFile', buildInfoPath)
}
else {
tscArgs.push('--incremental', 'false')
}
tscArgs.push('--project', projectPath)
try {
const { stdout, stderr } = await execFileAsync('pnpm', tscArgs, {
cwd: projectDirectory,
env: {
...process.env,
NODE_OPTIONS: process.env.NODE_OPTIONS ?? '--max-old-space-size=8192',
},
maxBuffer: 1024 * 1024 * 32,
})
const rawOutput = `${stdout}${stderr}`.trim()
return {
diagnostics: parseDiagnostics(rawOutput, projectDirectory),
exitCode: 0,
rawOutput,
}
}
catch (error) {
const exitCode = typeof error === 'object' && error && 'code' in error && typeof error.code === 'number'
? error.code
: 1
const stdout = typeof error === 'object' && error && 'stdout' in error && typeof error.stdout === 'string'
? error.stdout
: ''
const stderr = typeof error === 'object' && error && 'stderr' in error && typeof error.stderr === 'string'
? error.stderr
: ''
const rawOutput = `${stdout}${stderr}`.trim()
return {
diagnostics: parseDiagnostics(rawOutput, projectDirectory),
exitCode,
rawOutput,
}
}
}
function parseDiagnostics(rawOutput: string, projectDirectory: string): DiagnosticEntry[] {
return rawOutput
.split('\n')
.map(line => line.trim())
.flatMap((line) => {
const match = line.match(DIAGNOSTIC_PATTERN)
if (!match)
return []
return [{
code: Number(match[4]),
fileName: path.resolve(projectDirectory, match[1]!),
line: Number(match[2]),
message: match[5] ?? '',
}]
})
}
function summarizeCodes(diagnostics: DiagnosticEntry[]): string {
const counts = new Map<number, number>()
for (const diagnostic of diagnostics)
counts.set(diagnostic.code, (counts.get(diagnostic.code) ?? 0) + 1)
return Array.from(counts.entries())
.sort((left, right) => right[1] - left[1])
.slice(0, 8)
.map(([code, count]) => `TS${code}:${count}`)
.join(', ')
}
function chunk<T>(items: T[], size: number): T[][] {
const batches: T[][] = []
for (let i = 0; i < items.length; i += size)
batches.push(items.slice(i, i + size))
return batches
}
async function runBatchMigration(options: CliOptions) {
for (let round = 1; round <= options.maxRounds; round += 1) {
const { diagnostics, exitCode, rawOutput } = await runTypeCheck(options.project)
if (exitCode === 0) {
const finalCheck = await runTypeCheck(options.project, { incremental: false })
if (finalCheck.exitCode !== 0) {
const finalDiagnostics = finalCheck.diagnostics
console.log(`Final cold type check found ${finalDiagnostics.length} diagnostic(s). ${summarizeCodes(finalDiagnostics)}`)
if (options.verbose) {
for (const diagnostic of finalDiagnostics.slice(0, 40))
console.log(`${path.relative(process.cwd(), diagnostic.fileName)}:${diagnostic.line} TS${diagnostic.code} ${diagnostic.message}`)
}
const finalSupportedFiles = Array.from(new Set(
finalDiagnostics
.filter(diagnostic => SUPPORTED_DIAGNOSTIC_CODES.has(diagnostic.code))
.map(diagnostic => diagnostic.fileName),
))
if (finalSupportedFiles.length > 0) {
console.log(` Final pass batch: ${finalSupportedFiles.length} file(s)`)
let finalResult = await runMigration({
files: finalSupportedFiles,
maxIterations: options.batchIterations,
project: options.project,
verbose: options.verbose,
write: true,
})
if (finalResult.totalEdits === 0) {
console.log(' No edits produced; retrying final pass with full project roots.')
finalResult = await runMigration({
files: finalSupportedFiles,
maxIterations: options.batchIterations,
project: options.project,
useFullProjectRoots: true,
verbose: options.verbose,
write: true,
})
}
if (finalResult.totalEdits > 0)
continue
}
if (finalCheck.rawOutput)
process.stderr.write(`${finalCheck.rawOutput}\n`)
process.exitCode = 1
return
}
console.log(`Type check passed after ${round - 1} migration round(s).`)
return
}
const supportedDiagnostics = diagnostics.filter(diagnostic => SUPPORTED_DIAGNOSTIC_CODES.has(diagnostic.code))
const unsupportedDiagnostics = diagnostics.filter(diagnostic => !SUPPORTED_DIAGNOSTIC_CODES.has(diagnostic.code))
const supportedFiles = Array.from(new Set(supportedDiagnostics.map(diagnostic => diagnostic.fileName)))
console.log(`Round ${round}: ${diagnostics.length} diagnostic(s). ${summarizeCodes(diagnostics)}`)
if (options.verbose) {
for (const diagnostic of diagnostics.slice(0, 40))
console.log(`${path.relative(process.cwd(), diagnostic.fileName)}:${diagnostic.line} TS${diagnostic.code} ${diagnostic.message}`)
}
if (supportedFiles.length === 0) {
console.error('No supported diagnostics remain to migrate.')
if (unsupportedDiagnostics.length > 0) {
console.error('Remaining unsupported diagnostics:')
for (const diagnostic of unsupportedDiagnostics.slice(0, 40))
console.error(`${path.relative(process.cwd(), diagnostic.fileName)}:${diagnostic.line} TS${diagnostic.code} ${diagnostic.message}`)
}
if (rawOutput)
process.stderr.write(`${rawOutput}\n`)
process.exitCode = 1
return
}
let roundEdits = 0
const batches = chunk(supportedFiles, options.batchSize)
for (const [index, batch] of batches.entries()) {
console.log(` Batch ${index + 1}/${batches.length}: ${batch.length} file(s)`)
let result = await runMigration({
files: batch,
maxIterations: options.batchIterations,
project: options.project,
verbose: options.verbose,
write: true,
})
if (result.totalEdits === 0) {
console.log(' No edits produced; retrying batch with full project roots.')
result = await runMigration({
files: batch,
maxIterations: options.batchIterations,
project: options.project,
useFullProjectRoots: true,
verbose: options.verbose,
write: true,
})
}
roundEdits += result.totalEdits
}
if (roundEdits === 0) {
console.error('Migration script made no edits in this round; stopping to avoid an infinite loop.')
process.exitCode = 1
return
}
}
console.error(`Reached --max-rounds=${options.maxRounds} before type check passed.`)
process.exitCode = 1
}
export async function runBatchMigrationCommand(argv: string[]) {
await runBatchMigration(parseArgs(argv))
}

View File

@ -0,0 +1,17 @@
import { defineConfig } from 'vite-plus'
export default defineConfig({
pack: {
clean: true,
deps: {
neverBundle: ['typescript'],
},
entry: ['src/cli.ts'],
format: ['esm'],
outDir: 'dist',
platform: 'node',
sourcemap: true,
target: 'node22',
treeshake: true,
},
})

16
pnpm-lock.yaml generated
View File

@ -624,6 +624,22 @@ importers:
specifier: 'catalog:'
version: 0.1.2
packages/migrate-no-unchecked-indexed-access:
dependencies:
typescript:
specifier: 'catalog:'
version: 6.0.2
devDependencies:
'@types/node':
specifier: 'catalog:'
version: 25.6.0
vite:
specifier: npm:@voidzero-dev/vite-plus-core@0.1.18
version: '@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)'
vite-plus:
specifier: 'catalog:'
version: 0.1.18(@types/node@25.6.0)(@vitest/coverage-v8@4.1.4)(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(happy-dom@20.9.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)
sdks/nodejs-client:
devDependencies:
'@eslint/js':

View File

@ -311,7 +311,7 @@ describe('Dataset Settings Flow - Cross-Module Configuration Cascade', () => {
await result.current.handleSave()
})
const savedBody = mockUpdateDatasetSetting.mock.calls[0][0].body as Record<string, unknown>
const savedBody = mockUpdateDatasetSetting.mock.calls[0]![0].body as Record<string, unknown>
expect(savedBody).not.toHaveProperty('partial_member_list')
})
})

View File

@ -109,7 +109,7 @@ describe('Document Management Flow', () => {
})
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(update.options.history).toBe('replace')
expect(update.searchParams.get('keyword')).toBe('test')
expect(update.searchParams.get('page')).toBe('2')
@ -123,7 +123,7 @@ describe('Document Management Flow', () => {
})
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(update.options.history).toBe('replace')
expect(update.searchParams.toString()).toBe('')
})

View File

@ -282,7 +282,7 @@ describe('Hit Testing Flow', () => {
const response = createHitTestingResponse(5)
for (let i = 1; i < response.records.length; i++) {
expect(response.records[i - 1].score).toBeGreaterThanOrEqual(response.records[i].score)
expect(response.records[i - 1]!.score).toBeGreaterThanOrEqual(response.records[i]!.score)
}
})
@ -290,8 +290,8 @@ describe('Hit Testing Flow', () => {
const response = createHitTestingResponse(1)
const record = response.records[0]
expect(record.segment.document.name).toBeTruthy()
expect(record.segment.document.data_source_type).toBeTruthy()
expect(record!.segment.document.name).toBeTruthy()
expect(record!.segment.document.data_source_type).toBeTruthy()
})
})

View File

@ -113,8 +113,8 @@ describe('Pipeline Data Source Store Composition - Cross-Slice Integration', ()
store.getState().setLocalFileList(files)
expect(store.getState().localFileList).toHaveLength(3)
expect(store.getState().localFileList[0].fileID).toBe('f1')
expect(store.getState().localFileList[2].fileID).toBe('f3')
expect(store.getState().localFileList[0]!.fileID).toBe('f1')
expect(store.getState().localFileList[2]!.fileID).toBe('f3')
})
it('should update preview ref when setting file list', () => {
@ -157,7 +157,7 @@ describe('Pipeline Data Source Store Composition - Cross-Slice Integration', ()
store.getState().setOnlineDocuments(pages)
expect(store.getState().onlineDocuments).toHaveLength(2)
expect(store.getState().onlineDocuments[0].page_id).toBe('page-1')
expect(store.getState().onlineDocuments[0]!.page_id).toBe('page-1')
})
it('should update preview ref when setting online documents', () => {
@ -391,7 +391,7 @@ describe('Pipeline Data Source Store Composition - Cross-Slice Integration', ()
store.getState().setLocalFileList(files)
// Step 3: Select current file for preview
store.getState().setCurrentLocalFile(files[0].file)
store.getState().setCurrentLocalFile(files[0]!.file)
// Verify all state is consistent
expect(store.getState().currentCredentialId).toBe('upload-cred-1')

View File

@ -289,7 +289,7 @@ describe('Segment CRUD Flow', () => {
// Open detail modal
act(() => {
modalResult.current.onClickCard(segments[0])
modalResult.current.onClickCard(segments[0]!)
})
// All states should be independent

View File

@ -146,7 +146,7 @@ describe('Document Detail Navigation Fix Verification', () => {
fireEvent.click(screen.getByTestId('back-button-fixed'))
// URLSearchParams will normalize the encoding, but preserve all parameters
const expectedCall = mockPush.mock.calls[0][0]
const expectedCall = mockPush.mock.calls[0]![0]
expect(expectedCall).toMatch(/^\/datasets\/dataset-123\/documents\?/)
expect(expectedCall).toMatch(/page=1/)
expect(expectedCall).toMatch(/limit=50/)
@ -257,7 +257,7 @@ describe('Document Detail Navigation Fix Verification', () => {
// Should still attempt navigation (URLSearchParams will clean up the parameters)
expect(mockPush).toHaveBeenCalled()
const navigationPath = mockPush.mock.calls[0][0]
const navigationPath = mockPush.mock.calls[0]![0]
expect(navigationPath).toMatch(/^\/datasets\/dataset-123\/documents/)
console.log('✅ Malformed parameters handled gracefully:', navigationPath)

View File

@ -175,16 +175,16 @@ describe('Explore App List Flow', () => {
it('should display all apps when no category filter is applied', () => {
renderAppList()
expect(screen.getByText('Writer Bot')).toBeInTheDocument()
expect(screen.getByText('Translator')).toBeInTheDocument()
expect(screen.getByText('Code Helper')).toBeInTheDocument()
expect(screen.getByText('Writer Bot'))!.toBeInTheDocument()
expect(screen.getByText('Translator'))!.toBeInTheDocument()
expect(screen.getByText('Code Helper'))!.toBeInTheDocument()
})
it('should filter apps by selected category', () => {
mockTabValue = 'Writing'
renderAppList()
expect(screen.getByText('Writer Bot')).toBeInTheDocument()
expect(screen.getByText('Writer Bot'))!.toBeInTheDocument()
expect(screen.queryByText('Translator')).not.toBeInTheDocument()
expect(screen.queryByText('Code Helper')).not.toBeInTheDocument()
})
@ -196,7 +196,7 @@ describe('Explore App List Flow', () => {
fireEvent.change(input, { target: { value: 'trans' } })
await waitFor(() => {
expect(screen.getByText('Translator')).toBeInTheDocument()
expect(screen.getByText('Translator'))!.toBeInTheDocument()
expect(screen.queryByText('Writer Bot')).not.toBeInTheDocument()
expect(screen.queryByText('Code Helper')).not.toBeInTheDocument()
})
@ -218,7 +218,7 @@ describe('Explore App List Flow', () => {
renderAppList(true, onSuccess)
// Step 2: Click add to workspace button - opens create modal
fireEvent.click(screen.getAllByText('explore.appCard.addToWorkspace')[0])
fireEvent.click(screen.getAllByText('explore.appCard.addToWorkspace')[0]!)
// Step 3: Confirm creation in modal
fireEvent.click(await screen.findByTestId('confirm-create'))
@ -232,7 +232,8 @@ describe('Explore App List Flow', () => {
expect(mockHandleImportDSL).toHaveBeenCalledTimes(1)
// Step 6: DSL confirm modal appears and user confirms
expect(await screen.findByTestId('dsl-confirm-modal')).toBeInTheDocument()
// Step 6: DSL confirm modal appears and user confirms
expect(await screen.findByTestId('dsl-confirm-modal'))!.toBeInTheDocument()
fireEvent.click(screen.getByTestId('dsl-confirm'))
// Step 7: Flow completes successfully
@ -250,7 +251,7 @@ describe('Explore App List Flow', () => {
mockExploreData = undefined
const { unmount } = render(appListElement())
expect(screen.getByRole('status')).toBeInTheDocument()
expect(screen.getByRole('status'))!.toBeInTheDocument()
// Step 2: Data loads
mockIsLoading = false
@ -262,7 +263,7 @@ describe('Explore App List Flow', () => {
renderAppList()
expect(screen.queryByRole('status')).not.toBeInTheDocument()
expect(screen.getByText('Alpha')).toBeInTheDocument()
expect(screen.getByText('Alpha'))!.toBeInTheDocument()
})
})

View File

@ -108,8 +108,8 @@ describe('Slash Command Dual-Mode System', () => {
const results = await handler?.search('', 'en')
expect(results).toHaveLength(2)
expect(results?.[0].title).toBe('Light Theme')
expect(results?.[1].title).toBe('Dark Theme')
expect(results?.[0]!.title).toBe('Light Theme')
expect(results?.[1]!.title).toBe('Dark Theme')
})
it('should not have execute function for submenu mode', () => {

View File

@ -145,15 +145,15 @@ describe('Plugin Data Utilities Integration', () => {
validCategory: getValidCategoryKeys(p.category),
}))
expect(results[0].validTags.length).toBeGreaterThan(0)
expect(results[0].validCategory).toBe('tool')
expect(results[0]!.validTags.length).toBeGreaterThan(0)
expect(results[0]!.validCategory).toBe('tool')
expect(results[1].validTags).toContain('image')
expect(results[1].validTags).toContain('design')
expect(results[1].validCategory).toBe('model')
expect(results[1]!.validTags).toContain('image')
expect(results[1]!.validTags).toContain('design')
expect(results[1]!.validCategory).toBe('model')
expect(results[2].validTags).toHaveLength(0)
expect(results[2].validCategory).toBe('extension')
expect(results[2]!.validTags).toHaveLength(0)
expect(results[2]!.validCategory).toBe('extension')
})
})
})

View File

@ -132,16 +132,16 @@ describe('Plugin Page Shell Flow', () => {
it('switches from installed plugins to marketplace and syncs the active tab into the URL', async () => {
const { onUrlUpdate } = renderPluginPage()
expect(screen.getByTestId('plugins-view')).toBeInTheDocument()
expect(screen.getByTestId('plugins-view'))!.toBeInTheDocument()
expect(screen.queryByTestId('marketplace-view')).not.toBeInTheDocument()
fireEvent.click(screen.getByTestId('tab-item-discover'))
await waitFor(() => {
expect(screen.getByTestId('marketplace-view')).toBeInTheDocument()
expect(screen.getByTestId('marketplace-view'))!.toBeInTheDocument()
})
const tabUpdate = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const tabUpdate = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(tabUpdate.searchParams.get('tab')).toBe('discover')
})
@ -150,13 +150,13 @@ describe('Plugin Page Shell Flow', () => {
await waitFor(() => {
expect(mockFetchManifestFromMarketPlace).toHaveBeenCalledWith('langgenius%2Fplugin-demo')
expect(screen.getByTestId('install-from-marketplace-modal')).toBeInTheDocument()
expect(screen.getByTestId('install-from-marketplace-modal'))!.toBeInTheDocument()
})
fireEvent.click(screen.getByRole('button', { name: 'close-install-modal' }))
await waitFor(() => {
const clearUpdate = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const clearUpdate = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(clearUpdate.searchParams.has('package-ids')).toBe(false)
})
})

View File

@ -38,9 +38,9 @@ describe('Chunk Preview Formatting', () => {
expect(Array.isArray(result)).toBe(true)
const chunks = result as Array<{ content: string, summary?: string }>
expect(chunks).toHaveLength(2)
expect(chunks[0].content).toBe('Chunk 1 content')
expect(chunks[0].summary).toBe('Summary 1')
expect(chunks[1].content).toBe('Chunk 2 content')
expect(chunks[0]!.content).toBe('Chunk 1 content')
expect(chunks[0]!.summary).toBe('Summary 1')
expect(chunks[1]!.content).toBe('Chunk 2 content')
})
it('should limit chunks to RAG_PIPELINE_PREVIEW_CHUNK_NUM', () => {
@ -84,9 +84,9 @@ describe('Chunk Preview Formatting', () => {
expect(result.parent_mode).toBe('paragraph')
expect(result.parent_child_chunks).toHaveLength(1)
expect(result.parent_child_chunks[0].parent_content).toBe('Parent paragraph')
expect(result.parent_child_chunks[0].parent_summary).toBe('Parent summary')
expect(result.parent_child_chunks[0].child_contents).toEqual(['Child 1', 'Child 2'])
expect(result.parent_child_chunks[0]!.parent_content).toBe('Parent paragraph')
expect(result.parent_child_chunks[0]!.parent_summary).toBe('Parent summary')
expect(result.parent_child_chunks[0]!.child_contents).toEqual(['Child 1', 'Child 2'])
})
it('should limit parent chunks in paragraph mode', () => {
@ -129,8 +129,8 @@ describe('Chunk Preview Formatting', () => {
}
expect(result.parent_child_chunks).toHaveLength(1)
expect(result.parent_child_chunks[0].parent_content).toBe('Full document content')
expect(result.parent_child_chunks[0].parent_mode).toBe('full-doc')
expect(result.parent_child_chunks[0]!.parent_content).toBe('Full document content')
expect(result.parent_child_chunks[0]!.parent_mode).toBe('full-doc')
})
it('should limit child chunks in full-doc mode', () => {
@ -149,7 +149,7 @@ describe('Chunk Preview Formatting', () => {
parent_child_chunks: Array<{ child_contents: string[] }>
}
expect(result.parent_child_chunks[0].child_contents).toHaveLength(3) // Mocked limit
expect(result.parent_child_chunks[0]!.child_contents).toHaveLength(3) // Mocked limit
})
})
@ -168,8 +168,8 @@ describe('Chunk Preview Formatting', () => {
}
expect(result.qa_chunks).toHaveLength(2)
expect(result.qa_chunks[0].question).toBe('What is AI?')
expect(result.qa_chunks[0].answer).toBe('Artificial Intelligence is...')
expect(result.qa_chunks[0]!.question).toBe('What is AI?')
expect(result.qa_chunks[0]!.answer).toBe('Artificial Intelligence is...')
})
it('should limit QA chunks', () => {

View File

@ -268,11 +268,11 @@ describe('Input Field CRUD Flow', () => {
const restoredFields = formDataList.map(fd => convertFormDataToINputField(fd))
expect(restoredFields).toHaveLength(2)
expect(restoredFields[0].variable).toBe('name')
expect(restoredFields[0].default_value).toBe('Alice')
expect(restoredFields[1].variable).toBe('count')
expect(restoredFields[1].default_value).toBe('10')
expect(restoredFields[1].unit).toBe('items')
expect(restoredFields[0]!.variable).toBe('name')
expect(restoredFields[0]!.default_value).toBe('Alice')
expect(restoredFields[1]!.variable).toBe('count')
expect(restoredFields[1]!.default_value).toBe('10')
expect(restoredFields[1]!.unit).toBe('items')
})
})
})

View File

@ -139,8 +139,8 @@ describe('Test Run Flow Integration', () => {
const { result } = renderHook(() => useTestRunSteps())
expect(result.current.steps).toHaveLength(2)
expect(result.current.steps[0].value).toBe('dataSource')
expect(result.current.steps[1].value).toBe('documentProcessing')
expect(result.current.steps[0]!.value).toBe('dataSource')
expect(result.current.steps[1]!.value).toBe('documentProcessing')
})
})
@ -153,8 +153,8 @@ describe('Test Run Flow Integration', () => {
// Should only include DataSource nodes, not KnowledgeBase
expect(result.current).toHaveLength(2)
expect(result.current[0].value).toBe('ds-1')
expect(result.current[1].value).toBe('ds-2')
expect(result.current[0]!.value).toBe('ds-1')
expect(result.current[1]!.value).toBe('ds-2')
})
it('should include node data in options', async () => {
@ -163,8 +163,8 @@ describe('Test Run Flow Integration', () => {
)
const { result } = renderHook(() => useDatasourceOptions())
expect(result.current[0].label).toBe('Local Files')
expect(result.current[0].data.type).toBe(BlockEnum.DataSource)
expect(result.current[0]!.label).toBe('Local Files')
expect(result.current[0]!.data.type).toBe(BlockEnum.DataSource)
})
})

View File

@ -296,7 +296,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
// Wait for theme system to fully initialize
await waitFor(() => {
expect(screen.getByTestId('theme-indicator')).toHaveTextContent('Current Theme: dark')
expect(screen.getByTestId('theme-indicator'))!.toHaveTextContent('Current Theme: dark')
})
const finalState = {
@ -319,10 +319,10 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
)
await waitFor(() => {
expect(screen.getByTestId('theme-indicator')).toHaveTextContent('Current Theme: light')
expect(screen.getByTestId('theme-indicator'))!.toHaveTextContent('Current Theme: light')
})
expect(screen.getByTestId('visual-appearance')).toHaveTextContent('Appearance: light')
expect(screen.getByTestId('visual-appearance'))!.toHaveTextContent('Appearance: light')
})
it('handles system theme with dark preference', async () => {
@ -335,10 +335,10 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
)
await waitFor(() => {
expect(screen.getByTestId('theme-indicator')).toHaveTextContent('Current Theme: dark')
expect(screen.getByTestId('theme-indicator'))!.toHaveTextContent('Current Theme: dark')
})
expect(screen.getByTestId('visual-appearance')).toHaveTextContent('Appearance: dark')
expect(screen.getByTestId('visual-appearance'))!.toHaveTextContent('Appearance: dark')
})
it('handles system theme with light preference', async () => {
@ -351,10 +351,10 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
)
await waitFor(() => {
expect(screen.getByTestId('theme-indicator')).toHaveTextContent('Current Theme: light')
expect(screen.getByTestId('theme-indicator'))!.toHaveTextContent('Current Theme: light')
})
expect(screen.getByTestId('visual-appearance')).toHaveTextContent('Appearance: light')
expect(screen.getByTestId('visual-appearance'))!.toHaveTextContent('Appearance: light')
})
it('handles no stored theme (defaults to system)', async () => {
@ -367,7 +367,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
)
await waitFor(() => {
expect(screen.getByTestId('theme-indicator')).toHaveTextContent('Current Theme: light')
expect(screen.getByTestId('theme-indicator'))!.toHaveTextContent('Current Theme: light')
})
})
@ -384,7 +384,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
)
await waitFor(() => {
expect(screen.getByTestId('timing-status')).toHaveTextContent('Phase: CSR')
expect(screen.getByTestId('timing-status'))!.toHaveTextContent('Phase: CSR')
})
// Analyze timing and style changes
@ -395,7 +395,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
// Check if there are style changes (this is visible flicker)
const hasStyleChange = timingData.length > 1
&& timingData[0].styles.backgroundColor !== timingData[timingData.length - 1].styles.backgroundColor
&& timingData[0]!.styles.backgroundColor !== timingData[timingData.length - 1]!.styles.backgroundColor
if (hasStyleChange)
console.log('⚠️ Style changes detected - this causes visible flicker')
@ -420,7 +420,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
)
await waitFor(() => {
expect(screen.getByTestId('css-classes')).toHaveTextContent('bg-gray-900 text-white')
expect(screen.getByTestId('css-classes'))!.toHaveTextContent('bg-gray-900 text-white')
})
console.log('\n=== CSS Class Change Detection ===')
@ -430,12 +430,12 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
// Check if CSS classes have changed
const hasCSSChange = cssStates.length > 1
&& cssStates[0].className !== cssStates[cssStates.length - 1].className
&& cssStates[0]!.className !== cssStates[cssStates.length - 1]!.className
if (hasCSSChange) {
console.log('⚠️ CSS class changes detected - may cause style flicker')
console.log(`From: "${cssStates[0].className}"`)
console.log(`To: "${cssStates[cssStates.length - 1].className}"`)
console.log(`From: "${cssStates[0]!.className}"`)
console.log(`To: "${cssStates[cssStates.length - 1]!.className}"`)
}
expect(hasCSSChange).toBe(true) // We expect to see this change
@ -469,11 +469,12 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
// Should fallback gracefully without crashing
await waitFor(() => {
expect(screen.getByTestId('theme-indicator')).toBeInTheDocument()
expect(screen.getByTestId('theme-indicator'))!.toBeInTheDocument()
})
// Should default to light theme when localStorage fails
expect(screen.getByTestId('visual-appearance')).toHaveTextContent('Appearance: light')
// Should default to light theme when localStorage fails
expect(screen.getByTestId('visual-appearance'))!.toHaveTextContent('Appearance: light')
}
finally {
Reflect.deleteProperty(window, 'localStorage')
@ -490,12 +491,12 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
)
await waitFor(() => {
expect(screen.getByTestId('theme-indicator')).toBeInTheDocument()
expect(screen.getByTestId('theme-indicator'))!.toBeInTheDocument()
})
// Should handle invalid values gracefully
const themeIndicator = screen.getByTestId('theme-indicator')
expect(themeIndicator).toBeInTheDocument()
expect(themeIndicator)!.toBeInTheDocument()
})
})
@ -516,7 +517,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => {
)
await waitFor(() => {
expect(screen.getByTestId('performance-test')).toHaveTextContent('Theme: dark')
expect(screen.getByTestId('performance-test'))!.toHaveTextContent('Theme: dark')
})
// Analyze performance timeline

View File

@ -174,7 +174,7 @@ describe('Tool Provider List Shell Flow', () => {
fireEvent.click(screen.getByTestId('tool-card-plugin-tool'))
await waitFor(() => {
expect(screen.getByTestId('tool-plugin-detail-panel')).toBeInTheDocument()
expect(screen.getByTestId('tool-plugin-detail-panel'))!.toBeInTheDocument()
})
fireEvent.click(screen.getByRole('button', { name: 'update-plugin-detail' }))
@ -196,10 +196,10 @@ describe('Tool Provider List Shell Flow', () => {
fireEvent.click(screen.getByTestId('tab-item-workflow'))
await waitFor(() => {
expect(screen.getByTestId('workflow-empty')).toBeInTheDocument()
expect(screen.getByTestId('workflow-empty'))!.toBeInTheDocument()
})
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(update.searchParams.get('category')).toBe('workflow')
})
})

View File

@ -50,11 +50,11 @@ describe('Tool Data Processing Pipeline Integration', () => {
const formSchemas = toolParametersToFormSchemas(rawParameters as unknown as Parameters<typeof toolParametersToFormSchemas>[0])
expect(formSchemas).toHaveLength(2)
expect(formSchemas[0].variable).toBe('query')
expect(formSchemas[0].required).toBe(true)
expect(formSchemas[0].type).toBe('text-input')
expect(formSchemas[1].variable).toBe('limit')
expect(formSchemas[1].type).toBe('number-input')
expect(formSchemas[0]!.variable).toBe('query')
expect(formSchemas[0]!.required).toBe(true)
expect(formSchemas[0]!.type).toBe('text-input')
expect(formSchemas[1]!.variable).toBe('limit')
expect(formSchemas[1]!.type).toBe('number-input')
const withDefaults = addDefaultValue({}, formSchemas)
expect(withDefaults.query).toBe('hello')
@ -83,9 +83,9 @@ describe('Tool Data Processing Pipeline Integration', () => {
const credentialSchemas = toolCredentialToFormSchemas(rawCredentials as Parameters<typeof toolCredentialToFormSchemas>[0])
expect(credentialSchemas).toHaveLength(1)
expect(credentialSchemas[0].variable).toBe('api_key')
expect(credentialSchemas[0].required).toBe(true)
expect(credentialSchemas[0].type).toBe('secret-input')
expect(credentialSchemas[0]!.variable).toBe('api_key')
expect(credentialSchemas[0]!.required).toBe(true)
expect(credentialSchemas[0]!.type).toBe('secret-input')
})
it('processes trigger event parameters through the pipeline', () => {
@ -107,9 +107,9 @@ describe('Tool Data Processing Pipeline Integration', () => {
const schemas = triggerEventParametersToFormSchemas(rawParams as unknown as Parameters<typeof triggerEventParametersToFormSchemas>[0])
expect(schemas).toHaveLength(1)
expect(schemas[0].name).toBe('event_type')
expect(schemas[0].type).toBe('select')
expect(schemas[0].options).toHaveLength(2)
expect(schemas[0]!.name).toBe('event_type')
expect(schemas[0]!.type).toBe('select')
expect(schemas[0]!.options).toHaveLength(2)
})
})
@ -189,16 +189,16 @@ describe('Tool Data Processing Pipeline Integration', () => {
] as Parameters<typeof addFileInfos>[1]
const sorted = sortAgentSorts(thoughts)
expect(sorted[0].id).toBe('t1')
expect(sorted[1].id).toBe('t2')
expect(sorted[2].id).toBe('t3')
expect(sorted[0]!.id).toBe('t1')
expect(sorted[1]!.id).toBe('t2')
expect(sorted[2]!.id).toBe('t3')
const enriched = addFileInfos(sorted, messageFiles)
expect(enriched[0].message_files).toBeUndefined()
expect(enriched[1].message_files).toHaveLength(1)
expect(enriched[1].message_files![0].id).toBe('f2')
expect(enriched[2].message_files).toHaveLength(1)
expect(enriched[2].message_files![0].id).toBe('f1')
expect(enriched[0]!.message_files).toBeUndefined()
expect(enriched[1]!.message_files).toHaveLength(1)
expect(enriched[1]!.message_files![0]!.id).toBe('f2')
expect(enriched[2]!.message_files).toHaveLength(1)
expect(enriched[2]!.message_files![0]!.id).toBe('f1')
})
it('handles null inputs gracefully in the pipeline', () => {

View File

@ -151,7 +151,7 @@ export default function OAuthAuthorize() {
return (
<div key={scope} className="flex items-center gap-2 body-sm-medium text-text-secondary">
{Icon ? <Icon.icon className="h-4 w-4" /> : <RiAccountCircleLine className="h-4 w-4" />}
{Icon.label}
{Icon!.label}
</div>
)
})}

View File

@ -89,7 +89,7 @@ describe('AppInfo', () => {
it('should render trigger when not onlyShowDetail', () => {
render(<AppInfo expand />)
expect(screen.getByTestId('trigger')).toBeInTheDocument()
expect(screen.getByTestId('trigger'))!.toBeInTheDocument()
})
it('should not render trigger when onlyShowDetail is true', () => {
@ -99,11 +99,11 @@ describe('AppInfo', () => {
it('should pass expand prop to trigger', () => {
render(<AppInfo expand />)
expect(screen.getByTestId('trigger')).toHaveAttribute('data-expand', 'true')
expect(screen.getByTestId('trigger'))!.toHaveAttribute('data-expand', 'true')
const { unmount } = render(<AppInfo expand={false} />)
const triggers = screen.getAllByTestId('trigger')
expect(triggers[triggers.length - 1]).toHaveAttribute('data-expand', 'false')
expect(triggers[triggers.length - 1])!.toHaveAttribute('data-expand', 'false')
unmount()
})
@ -114,7 +114,7 @@ describe('AppInfo', () => {
await user.click(screen.getByTestId('trigger'))
expect(mockSetPanelOpen).toHaveBeenCalled()
const updater = mockSetPanelOpen.mock.calls[0][0] as (v: boolean) => boolean
const updater = mockSetPanelOpen.mock.calls[0]![0] as (v: boolean) => boolean
expect(updater(false)).toBe(true)
expect(updater(true)).toBe(false)
})
@ -132,12 +132,12 @@ describe('AppInfo', () => {
it('should show detail panel based on panelOpen when not onlyShowDetail', () => {
mockUseAppInfoActions.panelOpen = true
render(<AppInfo expand />)
expect(screen.getByTestId('detail-panel')).toBeInTheDocument()
expect(screen.getByTestId('detail-panel'))!.toBeInTheDocument()
})
it('should show detail panel based on openState when onlyShowDetail', () => {
render(<AppInfo expand onlyShowDetail openState />)
expect(screen.getByTestId('detail-panel')).toBeInTheDocument()
expect(screen.getByTestId('detail-panel'))!.toBeInTheDocument()
})
it('should hide detail panel when openState is false and onlyShowDetail', () => {

View File

@ -64,7 +64,7 @@ describe('List', () => {
)
const checkboxes = getCheckboxes(container)
fireEvent.click(checkboxes[1])
fireEvent.click(checkboxes[1]!)
expect(onSelectedIdsChange).toHaveBeenCalledWith(['a'])
rerender(
@ -79,10 +79,10 @@ describe('List', () => {
/>,
)
const updatedCheckboxes = getCheckboxes(container)
fireEvent.click(updatedCheckboxes[1])
fireEvent.click(updatedCheckboxes[1]!)
expect(onSelectedIdsChange).toHaveBeenCalledWith([])
fireEvent.click(updatedCheckboxes[0])
fireEvent.click(updatedCheckboxes[0]!)
expect(onSelectedIdsChange).toHaveBeenCalledWith(['a', 'b'])
})
@ -103,13 +103,13 @@ describe('List', () => {
const row = screen.getByText(item.question).closest('tr') as HTMLTableRowElement
const actionButtons = within(row).getAllByRole('button')
fireEvent.click(actionButtons[1])
fireEvent.click(actionButtons[1]!)
expect(await screen.findByText('appDebug.feature.annotation.removeConfirm')).toBeInTheDocument()
expect(await screen.findByText('appDebug.feature.annotation.removeConfirm'))!.toBeInTheDocument()
const confirmButton = await screen.findByRole('button', { name: 'common.operation.confirm' })
fireEvent.click(confirmButton)
expect(onRemove).toHaveBeenCalledWith(item.id)
expect(screen.getByText('appAnnotation.batchAction.selected')).toBeInTheDocument()
expect(screen.getByText('appAnnotation.batchAction.selected'))!.toBeInTheDocument()
})
})

View File

@ -96,15 +96,15 @@ describe('BatchModal', () => {
renderComponent()
expect(screen.getByTestId('annotation-full')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'appAnnotation.batchModal.run' })).toBeDisabled()
expect(screen.getByTestId('annotation-full'))!.toBeInTheDocument()
expect(screen.getByRole('button', { name: 'appAnnotation.batchModal.run' }))!.toBeDisabled()
})
it('should reset uploader state when modal closes and allow manual cancellation', () => {
const { rerender, props } = renderComponent()
fireEvent.click(screen.getByTestId('mock-uploader'))
expect(screen.getByTestId('selected-file')).toHaveTextContent('batch.csv')
expect(screen.getByTestId('selected-file'))!.toHaveTextContent('batch.csv')
rerender(<BatchModal {...props} isShow={false} />)
rerender(<BatchModal {...props} isShow />)
@ -137,7 +137,7 @@ describe('BatchModal', () => {
expect(annotationBatchImportMock).toHaveBeenCalledTimes(1)
})
const formData = annotationBatchImportMock.mock.calls[0][0].body as FormData
const formData = annotationBatchImportMock.mock.calls[0]![0].body as FormData
expect(formData.get('file')).toBe(lastUploadedFile)
await waitFor(() => {

View File

@ -75,7 +75,8 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
// Assert - Check for modal title as it appears in the mock
expect(screen.getByText('appAnnotation.editModal.title')).toBeInTheDocument()
// Assert - Check for modal title as it appears in the mock
expect(screen.getByText('appAnnotation.editModal.title'))!.toBeInTheDocument()
})
it('should not render modal when isShow is false', () => {
@ -85,6 +86,37 @@ describe('EditAnnotationModal', () => {
// Act
render(<EditAnnotationModal {...props} />)
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
expect(screen.queryByText('appAnnotation.editModal.title')).not.toBeInTheDocument()
})
@ -97,8 +129,9 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
// Assert - Look for query and answer content
expect(screen.getByText('Test query')).toBeInTheDocument()
expect(screen.getByText('Test answer')).toBeInTheDocument()
// Assert - Look for query and answer content
expect(screen.getByText('Test query'))!.toBeInTheDocument()
expect(screen.getByText('Test answer'))!.toBeInTheDocument()
})
})
@ -116,8 +149,9 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
// Assert - Check content is displayed
expect(screen.getByText('Custom query content')).toBeInTheDocument()
expect(screen.getByText('Custom answer content')).toBeInTheDocument()
// Assert - Check content is displayed
expect(screen.getByText('Custom query content'))!.toBeInTheDocument()
expect(screen.getByText('Custom answer content'))!.toBeInTheDocument()
})
it('should show remove option when annotationId is provided', () => {
@ -131,7 +165,8 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
// Assert - Remove option should be present (using pattern)
expect(screen.getByText('appAnnotation.editModal.removeThisCache')).toBeInTheDocument()
// Assert - Remove option should be present (using pattern)
expect(screen.getByText('appAnnotation.editModal.removeThisCache'))!.toBeInTheDocument()
})
})
@ -160,7 +195,8 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
// Assert
expect(screen.getByText('appAnnotation.editModal.removeThisCache')).toBeInTheDocument()
// Assert
expect(screen.getByText('appAnnotation.editModal.removeThisCache'))!.toBeInTheDocument()
})
it('should save content when edited', async () => {
@ -183,7 +219,7 @@ describe('EditAnnotationModal', () => {
// Find and click edit link for query
const editLinks = screen.getAllByText(/common\.operation\.edit/i)
await user.click(editLinks[0])
await user.click(editLinks[0]!)
// Find textarea and enter new content
const textarea = screen.getByRole('textbox')
@ -225,7 +261,7 @@ describe('EditAnnotationModal', () => {
// Edit query content
const editLinks = screen.getAllByText(/common\.operation\.edit/i)
await user.click(editLinks[0])
await user.click(editLinks[0]!)
const textarea = screen.getByRole('textbox')
await user.clear(textarea)
@ -258,7 +294,7 @@ describe('EditAnnotationModal', () => {
// Edit query content
const editLinks = screen.getAllByText(/common\.operation\.edit/i)
await user.click(editLinks[0])
await user.click(editLinks[0]!)
const textarea = screen.getByRole('textbox')
await user.clear(textarea)
@ -289,6 +325,37 @@ describe('EditAnnotationModal', () => {
// Act
render(<EditAnnotationModal {...props} />)
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
// Assert - Confirm dialog should not be visible initially
expect(screen.queryByText('appDebug.feature.annotation.removeConfirm')).not.toBeInTheDocument()
})
@ -306,7 +373,8 @@ describe('EditAnnotationModal', () => {
await user.click(screen.getByText('appAnnotation.editModal.removeThisCache'))
// Assert - Confirmation dialog should appear
expect(screen.getByText('appDebug.feature.annotation.removeConfirm')).toBeInTheDocument()
// Assert - Confirmation dialog should appear
expect(screen.getByText('appDebug.feature.annotation.removeConfirm'))!.toBeInTheDocument()
})
it('should call onRemove when removal is confirmed', async () => {
@ -342,7 +410,7 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
await user.click(screen.getByText('appAnnotation.editModal.removeThisCache'))
expect(screen.getByText('appDebug.feature.annotation.removeConfirm')).toBeInTheDocument()
expect(screen.getByText('appDebug.feature.annotation.removeConfirm'))!.toBeInTheDocument()
await user.click(screen.getByRole('button', { name: 'common.operation.cancel' }))
@ -366,7 +434,8 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
// Assert
expect(screen.getByText('appAnnotation.editModal.title')).toBeInTheDocument()
// Assert
expect(screen.getByText('appAnnotation.editModal.title'))!.toBeInTheDocument()
})
it('should handle very long content', () => {
@ -383,8 +452,9 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
// Assert
expect(screen.getByText(longQuery)).toBeInTheDocument()
expect(screen.getByText(longAnswer)).toBeInTheDocument()
// Assert
expect(screen.getByText(longQuery))!.toBeInTheDocument()
expect(screen.getByText(longAnswer))!.toBeInTheDocument()
})
it('should handle special characters in content', () => {
@ -401,8 +471,9 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
// Assert
expect(screen.getByText(specialQuery)).toBeInTheDocument()
expect(screen.getByText(specialAnswer)).toBeInTheDocument()
// Assert
expect(screen.getByText(specialQuery))!.toBeInTheDocument()
expect(screen.getByText(specialAnswer))!.toBeInTheDocument()
})
it('should handle onlyEditResponse prop', () => {
@ -440,7 +511,7 @@ describe('EditAnnotationModal', () => {
// Find and click edit link for query
const editLinks = screen.getAllByText(/common\.operation\.edit/i)
await user.click(editLinks[0])
await user.click(editLinks[0]!)
// Find textarea and enter new content
const textarea = screen.getByRole('textbox')
@ -458,8 +529,9 @@ describe('EditAnnotationModal', () => {
expect(mockOnAdded).not.toHaveBeenCalled()
// Verify edit mode remains open (textarea should still be visible)
expect(screen.getByRole('textbox')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.save' })).toBeInTheDocument()
// Verify edit mode remains open (textarea should still be visible)
expect(screen.getByRole('textbox'))!.toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.save' }))!.toBeInTheDocument()
})
it('should show fallback error message when addAnnotation error has no message', async () => {
@ -477,7 +549,7 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
const editLinks = screen.getAllByText(/common\.operation\.edit/i)
await user.click(editLinks[0])
await user.click(editLinks[0]!)
const textarea = screen.getByRole('textbox')
await user.clear(textarea)
@ -493,8 +565,9 @@ describe('EditAnnotationModal', () => {
expect(mockOnAdded).not.toHaveBeenCalled()
// Verify edit mode remains open (textarea should still be visible)
expect(screen.getByRole('textbox')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.save' })).toBeInTheDocument()
// Verify edit mode remains open (textarea should still be visible)
expect(screen.getByRole('textbox'))!.toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.save' }))!.toBeInTheDocument()
})
it('should show error toast and skip callbacks when editAnnotation fails', async () => {
@ -516,7 +589,7 @@ describe('EditAnnotationModal', () => {
// Edit query content
const editLinks = screen.getAllByText(/common\.operation\.edit/i)
await user.click(editLinks[0])
await user.click(editLinks[0]!)
const textarea = screen.getByRole('textbox')
await user.clear(textarea)
@ -532,8 +605,9 @@ describe('EditAnnotationModal', () => {
expect(mockOnEdited).not.toHaveBeenCalled()
// Verify edit mode remains open (textarea should still be visible)
expect(screen.getByRole('textbox')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.save' })).toBeInTheDocument()
// Verify edit mode remains open (textarea should still be visible)
expect(screen.getByRole('textbox'))!.toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.save' }))!.toBeInTheDocument()
})
it('should show fallback error message when editAnnotation error is not an Error instance', async () => {
@ -553,7 +627,7 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
const editLinks = screen.getAllByText(/common\.operation\.edit/i)
await user.click(editLinks[0])
await user.click(editLinks[0]!)
const textarea = screen.getByRole('textbox')
await user.clear(textarea)
@ -569,8 +643,9 @@ describe('EditAnnotationModal', () => {
expect(mockOnEdited).not.toHaveBeenCalled()
// Verify edit mode remains open (textarea should still be visible)
expect(screen.getByRole('textbox')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.save' })).toBeInTheDocument()
// Verify edit mode remains open (textarea should still be visible)
expect(screen.getByRole('textbox'))!.toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.save' }))!.toBeInTheDocument()
})
})
@ -589,7 +664,7 @@ describe('EditAnnotationModal', () => {
// Assert - Check that the formatted time appears somewhere in the component
const container = screen.getByRole('dialog')
expect(container).toHaveTextContent('2023-12-01 10:30:00')
expect(container)!.toHaveTextContent('2023-12-01 10:30:00')
})
it('should not show createdAt when not provided', () => {
@ -619,7 +694,8 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
// Assert - Should have remove functionality
expect(screen.getByText('appAnnotation.editModal.removeThisCache')).toBeInTheDocument()
// Assert - Should have remove functionality
expect(screen.getByText('appAnnotation.editModal.removeThisCache'))!.toBeInTheDocument()
})
})
@ -634,7 +710,7 @@ describe('EditAnnotationModal', () => {
render(<EditAnnotationModal {...props} />)
const editLinks = screen.getAllByText(/common\.operation\.edit/i)
await user.click(editLinks[0])
await user.click(editLinks[0]!)
const textarea = screen.getByRole('textbox')
await user.clear(textarea)
@ -661,7 +737,8 @@ describe('EditAnnotationModal', () => {
rerender(<EditAnnotationModal {...props} />)
// Assert - Component should still be visible (no errors thrown)
expect(screen.getByText('appAnnotation.editModal.title')).toBeInTheDocument()
// Assert - Component should still be visible (no errors thrown)
expect(screen.getByText('appAnnotation.editModal.title'))!.toBeInTheDocument()
})
it('should re-render when props change', () => {
@ -674,7 +751,8 @@ describe('EditAnnotationModal', () => {
rerender(<EditAnnotationModal {...newProps} />)
// Assert - Should show new content
expect(screen.getByText('New query content')).toBeInTheDocument()
// Assert - Should show new content
expect(screen.getByText('New query content'))!.toBeInTheDocument()
})
})
})

View File

@ -172,7 +172,7 @@ type HeaderOptionsProps = ComponentProps<typeof HeaderOptions>
const renderComponent = (
props: Partial<HeaderOptionsProps> = {},
locale: Locale = LanguagesSupported[0],
locale: Locale = LanguagesSupported[0]!,
) => {
;(useLocale as Mock).mockReturnValue(locale)
@ -279,8 +279,8 @@ describe('HeaderOptions', () => {
const { csvButton, jsonButton } = await getExportButtons()
expect(csvButton).toBeDisabled()
expect(jsonButton).toBeDisabled()
expect(csvButton)!.toBeDisabled()
expect(jsonButton)!.toBeDisabled()
expect(lastCSVDownloaderProps).toMatchObject({
data: [['Question', 'Answer']],
@ -323,7 +323,7 @@ describe('HeaderOptions', () => {
await openOperationsPopover(user)
await clickOperationAction(user, 'appAnnotation.table.header.bulkImport')
expect(await screen.findByText('appAnnotation.batchModal.title')).toBeInTheDocument()
expect(await screen.findByText('appAnnotation.batchModal.title'))!.toBeInTheDocument()
await user.click(
screen.getByRole('button', { name: 'appAnnotation.batchModal.cancel' }),
)
@ -375,7 +375,7 @@ describe('HeaderOptions', () => {
})
const lines = blobContent.trim().split('\n')
expect(lines).toHaveLength(1)
expect(JSON.parse(lines[0])).toEqual({
expect(JSON.parse(lines[0]!)).toEqual({
messages: [
{ role: 'system', content: '' },
{ role: 'user', content: 'Question 1' },

View File

@ -126,7 +126,8 @@ describe('ViewAnnotationModal', () => {
fireEvent.click(screen.getByText('appAnnotation.viewModal.hitHistory'))
// Assert
expect(await screen.findByText('appAnnotation.viewModal.noHitHistory')).toBeInTheDocument()
// Assert
expect(await screen.findByText('appAnnotation.viewModal.noHitHistory'))!.toBeInTheDocument()
expect(mockFormatTime).toHaveBeenCalledWith(props.item.created_at, 'appLog.dateTimeFormat')
})
@ -138,16 +139,16 @@ describe('ViewAnnotationModal', () => {
fireEvent.click(await screen.findByText('appAnnotation.viewModal.hitHistory'))
expect(await screen.findByText('user input')).toBeInTheDocument()
expect(screen.getByText('15 appAnnotation.viewModal.hits')).toBeInTheDocument()
expect(mockFormatTime).toHaveBeenCalledWith(hits[0].created_at, 'appLog.dateTimeFormat')
expect(await screen.findByText('user input'))!.toBeInTheDocument()
expect(screen.getByText('15 appAnnotation.viewModal.hits'))!.toBeInTheDocument()
expect(mockFormatTime).toHaveBeenCalledWith(hits[0]!.created_at, 'appLog.dateTimeFormat')
})
it('should confirm before removing the annotation and hide on success', async () => {
const { props } = renderComponent()
fireEvent.click(screen.getByText('appAnnotation.editModal.removeThisCache'))
expect(await screen.findByText('appDebug.feature.annotation.removeConfirm')).toBeInTheDocument()
expect(await screen.findByText('appDebug.feature.annotation.removeConfirm'))!.toBeInTheDocument()
const confirmButton = await screen.findByRole('button', { name: 'common.operation.confirm' })
fireEvent.click(confirmButton)
@ -162,7 +163,7 @@ describe('ViewAnnotationModal', () => {
renderComponent()
fireEvent.click(screen.getByText('appAnnotation.editModal.removeThisCache'))
expect(await screen.findByText('appDebug.feature.annotation.removeConfirm')).toBeInTheDocument()
expect(await screen.findByText('appDebug.feature.annotation.removeConfirm'))!.toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' }))

View File

@ -36,7 +36,7 @@ export default function AddMemberOrGroupDialog() {
let observer: IntersectionObserver | undefined
if (anchorRef.current) {
observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && !isLoading && hasMore)
if (entries[0]!.isIntersecting && !isLoading && hasMore)
fetchNextPage()
}, { rootMargin: '20px' })
observer.observe(anchorRef.current)

View File

@ -207,7 +207,7 @@ describe('AppPublisher', () => {
fireEvent.click(screen.getByText('common.publish'))
expect(screen.getByText('publisher-summary-publish')).toBeInTheDocument()
expect(screen.getByText('publisher-summary-publish'))!.toBeInTheDocument()
expect(mockOnToggle).toHaveBeenCalledWith(true)
await waitFor(() => {
@ -248,7 +248,7 @@ describe('AppPublisher', () => {
fireEvent.click(screen.getByText('common.publish'))
fireEvent.click(screen.getByText('publisher-embed'))
expect(screen.getByTestId('embedded-modal')).toBeInTheDocument()
expect(screen.getByTestId('embedded-modal'))!.toBeInTheDocument()
})
it('should close embedded and access control panels through child callbacks', async () => {
@ -265,7 +265,7 @@ describe('AppPublisher', () => {
fireEvent.click(screen.getByText('common.publish'))
fireEvent.click(screen.getByText('publisher-access-control'))
expect(screen.getByTestId('access-control')).toBeInTheDocument()
expect(screen.getByTestId('access-control'))!.toBeInTheDocument()
fireEvent.click(screen.getByText('close-access-control'))
expect(screen.queryByTestId('access-control')).not.toBeInTheDocument()
})
@ -280,7 +280,7 @@ describe('AppPublisher', () => {
fireEvent.click(screen.getByText('common.publish'))
fireEvent.click(screen.getByText('publisher-access-control'))
expect(screen.getByTestId('access-control')).toBeInTheDocument()
expect(screen.getByTestId('access-control'))!.toBeInTheDocument()
fireEvent.click(screen.getByText('confirm-access-control'))
@ -338,7 +338,7 @@ describe('AppPublisher', () => {
/>,
)
ahooksMocks.keyPressHandlers[0]({ preventDefault })
ahooksMocks.keyPressHandlers[0]!({ preventDefault })
await waitFor(() => {
expect(preventDefault).toHaveBeenCalled()
@ -367,7 +367,7 @@ describe('AppPublisher', () => {
/>,
)
ahooksMocks.keyPressHandlers[0]({ preventDefault })
ahooksMocks.keyPressHandlers[0]!({ preventDefault })
await waitFor(() => {
expect(preventDefault).toHaveBeenCalled()
@ -381,7 +381,7 @@ describe('AppPublisher', () => {
await waitFor(() => {
expect(onRestore).toHaveBeenCalledTimes(1)
})
expect(screen.getByText('publisher-summary-publish')).toBeInTheDocument()
expect(screen.getByText('publisher-summary-publish'))!.toBeInTheDocument()
})
it('should report missing explore installations', async () => {
@ -455,6 +455,6 @@ describe('AppPublisher', () => {
await waitFor(() => {
expect(mockFetchAppDetailDirect).not.toHaveBeenCalled()
})
expect(screen.getByTestId('access-control')).toBeInTheDocument()
expect(screen.getByTestId('access-control'))!.toBeInTheDocument()
})
})

View File

@ -34,8 +34,8 @@ describe('VersionInfoModal', () => {
/>,
)
expect(screen.getByDisplayValue('Release 1')).toBeInTheDocument()
expect(screen.getByDisplayValue('Initial release')).toBeInTheDocument()
expect(screen.getByDisplayValue('Release 1'))!.toBeInTheDocument()
expect(screen.getByDisplayValue('Initial release'))!.toBeInTheDocument()
})
it('should reject overlong titles', () => {
@ -50,7 +50,7 @@ describe('VersionInfoModal', () => {
)
const [titleInput] = screen.getAllByRole('textbox')
fireEvent.change(titleInput, { target: { value: 'a'.repeat(16) } })
fireEvent.change(titleInput!, { target: { value: 'a'.repeat(16) } })
fireEvent.click(screen.getByRole('button', { name: 'common.publish' }))
expect(toast.error).toHaveBeenCalledWith('versionHistory.editField.titleLengthLimit')
@ -75,8 +75,8 @@ describe('VersionInfoModal', () => {
)
const [titleInput, notesInput] = screen.getAllByRole('textbox')
fireEvent.change(titleInput, { target: { value: 'Release 2' } })
fireEvent.change(notesInput, { target: { value: 'Updated notes' } })
fireEvent.change(titleInput!, { target: { value: 'Release 2' } })
fireEvent.change(notesInput!, { target: { value: 'Updated notes' } })
fireEvent.click(screen.getByRole('button', { name: 'common.publish' }))
expect(handlePublish).toHaveBeenCalledWith({
@ -106,16 +106,16 @@ describe('VersionInfoModal', () => {
const [titleInput, notesInput] = screen.getAllByRole('textbox')
fireEvent.change(titleInput, { target: { value: 'a'.repeat(16) } })
fireEvent.change(titleInput!, { target: { value: 'a'.repeat(16) } })
fireEvent.click(screen.getByRole('button', { name: 'common.publish' }))
expect(toast.error).toHaveBeenCalledWith('versionHistory.editField.titleLengthLimit')
fireEvent.change(titleInput, { target: { value: 'Release 3' } })
fireEvent.change(notesInput, { target: { value: 'b'.repeat(101) } })
fireEvent.change(titleInput!, { target: { value: 'Release 3' } })
fireEvent.change(notesInput!, { target: { value: 'b'.repeat(101) } })
fireEvent.click(screen.getByRole('button', { name: 'common.publish' }))
expect(toast.error).toHaveBeenCalledWith('versionHistory.editField.releaseNotesLengthLimit')
fireEvent.change(notesInput, { target: { value: 'Stable release notes' } })
fireEvent.change(notesInput!, { target: { value: 'Stable release notes' } })
fireEvent.click(screen.getByRole('button', { name: 'common.publish' }))
expect(handlePublish).toHaveBeenCalledWith({

View File

@ -62,7 +62,7 @@ const FeaturesWrappedAppPublisher = (props: Props) => {
},
enabled: !!(file_upload?.enabled || file_upload?.image?.enabled),
allowed_file_types: file_upload?.allowed_file_types || [SupportUploadFileTypes.image],
allowed_file_extensions: file_upload?.allowed_file_extensions || FILE_EXTS[SupportUploadFileTypes.image].map(ext => `.${ext}`),
allowed_file_extensions: file_upload?.allowed_file_extensions || FILE_EXTS[SupportUploadFileTypes.image]!.map(ext => `.${ext}`),
allowed_file_upload_methods: file_upload?.allowed_file_upload_methods || file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
number_limits: file_upload?.number_limits || file_upload?.image?.number_limits || 3,
} as FileUpload

View File

@ -197,7 +197,7 @@ const AppPublisher = ({
throw new Error('App not found')
const { installed_apps } = await fetchInstalledAppList(appDetail.id)
if (installed_apps?.length > 0)
return `${basePath}/explore/installed/${installed_apps[0].id}`
return `${basePath}/explore/installed/${installed_apps[0]!.id}`
throw new Error('No app found in Explore')
}, {
onError: (err) => {

View File

@ -60,8 +60,8 @@ describe('configuration utils', () => {
{ id: 'tool-2', icon: '/console/icons/prefixed.svg' },
] as never, '/console')
expect(result[0].icon).toBe('/console/icons/tool.svg')
expect(result[1].icon).toBe('/console/icons/prefixed.svg')
expect(result[0]!.icon).toBe('/console/icons/tool.svg')
expect(result[1]!.icon).toBe('/console/icons/prefixed.svg')
})
})

View File

@ -208,7 +208,7 @@ describe('AdvancedPromptInput', () => {
fireEvent.click(screen.getByText('open-advanced-tool-modal'))
const modalConfig = mockSetShowExternalDataToolModal.mock.calls[0][0]
const modalConfig = mockSetShowExternalDataToolModal.mock.calls[0]![0]
expect(modalConfig.onValidateBeforeSaveCallback({ variable: 'existing_var' })).toBe(false)
expect(mockToastError).toHaveBeenCalledWith('varKeyError.keyAlreadyExists')

View File

@ -144,8 +144,8 @@ describe('Prompt config component', () => {
renderComponent({ onChange }, { isAdvancedMode: false })
const simplePrompt = screen.getByTestId('simple-prompt-input')
expect(simplePrompt).toBeInTheDocument()
expect(simplePrompt).toHaveAttribute('data-mode', AppModeEnum.CHAT)
expect(simplePrompt)!.toBeInTheDocument()
expect(simplePrompt)!.toHaveAttribute('data-mode', AppModeEnum.CHAT)
expect(mockSimplePromptInputProps?.promptTemplate).toBe('initial template')
fireEvent.click(simplePrompt)
expect(onChange).toHaveBeenCalledWith('mocked prompt', defaultPromptVariables)
@ -171,9 +171,9 @@ describe('Prompt config component', () => {
const renderedMessages = screen.getAllByTestId('advanced-message-input')
expect(renderedMessages).toHaveLength(2)
expect(renderedMessages[0]).toHaveAttribute('data-context-missing', 'true')
fireEvent.click(screen.getAllByText('hide-context')[0])
expect(screen.getAllByTestId('advanced-message-input')[0]).toHaveAttribute('data-context-missing', 'false')
expect(renderedMessages[0])!.toHaveAttribute('data-context-missing', 'true')
fireEvent.click(screen.getAllByText('hide-context')[0]!)
expect(screen.getAllByTestId('advanced-message-input')[0])!.toHaveAttribute('data-context-missing', 'false')
})
// Chat message mutations
@ -193,7 +193,7 @@ describe('Prompt config component', () => {
},
)
fireEvent.click(screen.getAllByText('change')[0])
fireEvent.click(screen.getAllByText('change')[0]!)
expect(setCurrentAdvancedPrompt).toHaveBeenCalledWith(
[
{ role: PromptRole.user, text: 'updated text' },
@ -219,7 +219,7 @@ describe('Prompt config component', () => {
},
)
fireEvent.click(screen.getAllByText('type')[1])
fireEvent.click(screen.getAllByText('type')[1]!)
expect(setCurrentAdvancedPrompt).toHaveBeenCalledWith(
[
{ role: PromptRole.user, text: 'first' },
@ -244,7 +244,7 @@ describe('Prompt config component', () => {
},
)
fireEvent.click(screen.getAllByText('delete')[0])
fireEvent.click(screen.getAllByText('delete')[0]!)
expect(setCurrentAdvancedPrompt).toHaveBeenCalledWith([{ role: PromptRole.assistant, text: 'second' }])
})

View File

@ -151,7 +151,7 @@ describe('SimplePromptInput', () => {
fireEvent.click(screen.getByText('blur-prompt'))
expect(screen.getByText('autoAddVar')).toBeInTheDocument()
expect(screen.getByText('autoAddVar'))!.toBeInTheDocument()
fireEvent.click(screen.getByText('operation.add'))
@ -180,7 +180,7 @@ describe('SimplePromptInput', () => {
fireEvent.click(screen.getByText('open-tool-modal'))
expect(mockSetShowExternalDataToolModal).toHaveBeenCalledTimes(1)
const modalConfig = mockSetShowExternalDataToolModal.mock.calls[0][0]
const modalConfig = mockSetShowExternalDataToolModal.mock.calls[0]![0]
expect(modalConfig.onValidateBeforeSaveCallback({ variable: 'existing_var' })).toBe(false)
expect(mockToastError).toHaveBeenCalledWith('varKeyError.keyAlreadyExists')
@ -267,10 +267,10 @@ describe('SimplePromptInput', () => {
</ConfigContext.Provider>,
)
expect(screen.getByText('datasets:Knowledge Base')).toBeInTheDocument()
expect(screen.getByText('variables:existing_var')).toBeInTheDocument()
expect(screen.getByText('external-tools:search_api')).toBeInTheDocument()
expect(screen.getByText('query-selectable:false')).toBeInTheDocument()
expect(screen.getByText('datasets:Knowledge Base'))!.toBeInTheDocument()
expect(screen.getByText('variables:existing_var'))!.toBeInTheDocument()
expect(screen.getByText('external-tools:search_api'))!.toBeInTheDocument()
expect(screen.getByText('query-selectable:false'))!.toBeInTheDocument()
})
it('should skip external tool variables and incomplete prompt variables when deciding whether to auto add', () => {
@ -312,7 +312,7 @@ describe('SimplePromptInput', () => {
)
fireEvent.click(screen.getByText('blur-prompt'))
expect(screen.getByText('autoAddVar')).toBeInTheDocument()
expect(screen.getByText('autoAddVar'))!.toBeInTheDocument()
fireEvent.click(screen.getByText('operation.cancel'))
expect(mockOnChange).toHaveBeenCalledWith('Hello {{existing_var}}', [])

View File

@ -96,8 +96,8 @@ const AdvancedPromptInput: FC<Props> = ({
},
onValidateBeforeSaveCallback: (newExternalDataTool: ExternalDataTool) => {
for (let i = 0; i < promptVariables.length; i++) {
if (promptVariables[i].key === newExternalDataTool.variable) {
toast.error(t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: promptVariables[i].key }))
if (promptVariables[i]!.key === newExternalDataTool.variable) {
toast.error(t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: promptVariables[i]!.key }))
return false
}
}

View File

@ -53,7 +53,7 @@ const Prompt: FC<IPromptProps> = ({
const handleMessageTypeChange = (index: number, role: PromptRole) => {
const newPrompt = produce(currentAdvancedPrompt as PromptItem[], (draft) => {
draft[index].role = role
draft[index]!.role = role
})
setCurrentAdvancedPrompt(newPrompt)
}
@ -61,7 +61,7 @@ const Prompt: FC<IPromptProps> = ({
const handleValueChange = (value: string, index?: number) => {
if (modelModeType === ModelModeType.chat) {
const newPrompt = produce(currentAdvancedPrompt as PromptItem[], (draft) => {
draft[index as number].text = value
draft[index as number]!.text = value
})
setCurrentAdvancedPrompt(newPrompt, true)
}

View File

@ -94,8 +94,8 @@ const Prompt: FC<ISimplePromptInput> = ({
},
onValidateBeforeSaveCallback: (newExternalDataTool: ExternalDataTool) => {
for (let i = 0; i < promptVariables.length; i++) {
if (promptVariables[i].key === newExternalDataTool.variable) {
toast.error(t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: promptVariables[i].key }))
if (promptVariables[i]!.key === newExternalDataTool.variable) {
toast.error(t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: promptVariables[i]!.key }))
return false
}
}

View File

@ -118,7 +118,7 @@ describe('ConfigVar', () => {
it('should show empty state when no variables exist', () => {
renderConfigVar({ promptVariables: [] })
expect(screen.getByText('appDebug.notSetVar')).toBeInTheDocument()
expect(screen.getByText('appDebug.notSetVar'))!.toBeInTheDocument()
})
it('should render variable items and allow reordering via sortable list', () => {
@ -131,8 +131,8 @@ describe('ConfigVar', () => {
onPromptVariablesChange,
})
expect(screen.getByText('first')).toBeInTheDocument()
expect(screen.getByText('second')).toBeInTheDocument()
expect(screen.getByText('first'))!.toBeInTheDocument()
expect(screen.getByText('second'))!.toBeInTheDocument()
act(() => {
latestSortableProps?.setList([
@ -163,7 +163,7 @@ describe('ConfigVar', () => {
fireEvent.click(await screen.findByText('appDebug.variableConfig.string'))
expect(onPromptVariablesChange).toHaveBeenCalledTimes(1)
const [nextVariables] = onPromptVariablesChange.mock.calls[0]
const [nextVariables] = (onPromptVariablesChange.mock.calls[0] ?? []) as [any]
expect(nextVariables).toHaveLength(1)
expect(nextVariables[0].type).toBe('string')
})
@ -178,7 +178,7 @@ describe('ConfigVar', () => {
expect(onPromptVariablesChange).toHaveBeenCalledTimes(1)
expect(setShowExternalDataToolModal).toHaveBeenCalledTimes(1)
const modalState = setShowExternalDataToolModal.mock.calls[0][0]
const modalState = setShowExternalDataToolModal.mock.calls[0]![0]
expect(modalState.payload.type).toBe('api')
act(() => {
@ -197,13 +197,13 @@ describe('ConfigVar', () => {
fireEvent.click(screen.getByText('common.operation.add'))
fireEvent.click(await screen.findByText('appDebug.variableConfig.apiBasedVar'))
const modalState = setShowExternalDataToolModal.mock.calls[0][0]
const modalState = setShowExternalDataToolModal.mock.calls[0]![0]
act(() => {
modalState.onCancelCallback?.()
})
expect(onPromptVariablesChange).toHaveBeenCalledTimes(2)
const [addedVariables] = onPromptVariablesChange.mock.calls[0]
const [addedVariables] = (onPromptVariablesChange.mock.calls[0] ?? []) as [any]
expect(addedVariables).toHaveLength(2)
expect(addedVariables[0]).toBe(existingVar)
expect(addedVariables[1].type).toBe('api')
@ -235,7 +235,7 @@ describe('ConfigVar', () => {
expect(itemContainer).not.toBeNull()
const actionButtons = itemContainer!.querySelectorAll('div.h-6.w-6')
expect(actionButtons).toHaveLength(2)
fireEvent.click(actionButtons[0])
fireEvent.click(actionButtons[0]!)
const editDialog = await screen.findByRole('dialog')
const saveButton = within(editDialog).getByRole('button', { name: 'common.operation.save' })
@ -261,10 +261,10 @@ describe('ConfigVar', () => {
expect(itemContainer).not.toBeNull()
const actionButtons = itemContainer!.querySelectorAll('div.h-6.w-6')
expect(actionButtons).toHaveLength(2)
fireEvent.click(actionButtons[0])
fireEvent.click(actionButtons[0]!)
const inputs = await screen.findAllByPlaceholderText('appDebug.variableConfig.inputPlaceholder')
fireEvent.change(inputs[0], { target: { value: 'second' } })
fireEvent.change(inputs[0]!, { target: { value: 'second' } })
fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
@ -287,10 +287,10 @@ describe('ConfigVar', () => {
expect(itemContainer).not.toBeNull()
const actionButtons = itemContainer!.querySelectorAll('div.h-6.w-6')
expect(actionButtons).toHaveLength(2)
fireEvent.click(actionButtons[0])
fireEvent.click(actionButtons[0]!)
const inputs = await screen.findAllByPlaceholderText('appDebug.variableConfig.inputPlaceholder')
fireEvent.change(inputs[1], { target: { value: 'Second' } })
fireEvent.change(inputs[1]!, { target: { value: 'Second' } })
fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
@ -412,7 +412,7 @@ describe('ConfigVar', () => {
expect(itemContainer).not.toBeNull()
const actionButtons = itemContainer!.querySelectorAll('div.h-6.w-6')
fireEvent.click(actionButtons[0])
fireEvent.click(actionButtons[0]!)
const modalState = setShowExternalDataToolModal.mock.calls.at(-1)?.[0]
@ -461,7 +461,7 @@ describe('ConfigVar', () => {
expect(itemContainer).not.toBeNull()
const actionButtons = itemContainer!.querySelectorAll('div.h-6.w-6')
fireEvent.click(actionButtons[0])
fireEvent.click(actionButtons[0]!)
const modalState = setShowExternalDataToolModal.mock.calls.at(-1)?.[0]

View File

@ -178,8 +178,8 @@ describe('ConfigModalFormFields', () => {
render(<ConfigModalFormFields {...singleFileProps} />)
fireEvent.click(screen.getByText('single-file-setting'))
fireEvent.click(screen.getByText('upload-file'))
fireEvent.click(screen.getAllByText('unchecked')[0])
fireEvent.click(screen.getAllByText('unchecked')[1])
fireEvent.click(screen.getAllByText('unchecked')[0]!)
fireEvent.click(screen.getAllByText('unchecked')[1]!)
expect(singleFileProps.onFilePayloadChange).toHaveBeenCalledWith({ number_limits: 1 })
expect(singleFileProps.payloadChangeHandlers.default).toHaveBeenCalledWith(expect.objectContaining({
@ -198,7 +198,7 @@ describe('ConfigModalFormFields', () => {
}
render(<ConfigModalFormFields {...multiFileProps} />)
fireEvent.click(screen.getByText('multi-file-setting'))
fireEvent.click(screen.getAllByText('upload-file')[1])
fireEvent.click(screen.getAllByText('upload-file')[1]!)
expect(multiFileProps.onFilePayloadChange).toHaveBeenCalledWith({ number_limits: 3 })
expect(multiFileProps.payloadChangeHandlers.default).toHaveBeenCalledWith([
expect.objectContaining({ fileId: 'file-1' }),

View File

@ -44,9 +44,9 @@ describe('ConfigModal', () => {
)
const textboxes = screen.getAllByRole('textbox')
fireEvent.blur(textboxes[0], { target: { value: 'question' } })
fireEvent.blur(textboxes[0]!, { target: { value: 'question' } })
expect(textboxes[1]).toHaveValue('question')
expect(textboxes[1])!.toHaveValue('question')
})
it('should submit the edited payload when the form is valid', () => {

View File

@ -27,15 +27,15 @@ describe('ConfigString', () => {
const input = screen.getByRole('spinbutton')
expect(input).toHaveValue(3)
expect(input).toHaveAttribute('min', '1')
expect(input).toHaveAttribute('max', '8')
expect(input)!.toHaveValue(3)
expect(input)!.toHaveAttribute('min', '1')
expect(input)!.toHaveAttribute('max', '8')
})
it('should render empty input when value is undefined', () => {
const { onChange } = renderConfigString({ value: undefined })
expect(screen.getByRole('spinbutton')).toHaveValue(null)
expect(screen.getByRole('spinbutton'))!.toHaveValue(null)
expect(onChange).not.toHaveBeenCalled()
})
})
@ -116,7 +116,7 @@ describe('ConfigString', () => {
fireEvent.change(screen.getByRole('spinbutton'), { target: { value: '' } })
expect(onChange).toHaveBeenCalledTimes(1)
expect(onChange.mock.calls[0][0]).toBeNaN()
expect(onChange.mock.calls[0]![0]).toBeNaN()
})
})
})

View File

@ -165,8 +165,8 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
},
onValidateBeforeSaveCallback: (newExternalDataTool: ExternalDataTool) => {
for (let i = 0; i < promptVariables.length; i++) {
if (promptVariables[i].key === newExternalDataTool.variable && i !== index) {
toast.error(t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: promptVariables[i].key }))
if (promptVariables[i]!.key === newExternalDataTool.variable && i !== index) {
toast.error(t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: promptVariables[i]!.key }))
return false
}
}
@ -220,7 +220,7 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
const handleRemoveVar = useCallback((index: number) => {
const removeVar = promptVariables[index]
if (mode === AppModeEnum.COMPLETION && dataSets.length > 0 && removeVar.is_context_var) {
if (mode === AppModeEnum.COMPLETION && dataSets.length > 0 && removeVar!.is_context_var) {
showDeleteContextVarModal()
setRemoveIndex(index)
return

View File

@ -69,7 +69,7 @@ const setupFeatureStore = (fileOverrides: Partial<FileUpload> = {}) => {
const getLatestFileConfig = () => {
expect(setFeaturesMock).toHaveBeenCalled()
const latestFeatures = setFeaturesMock.mock.calls[setFeaturesMock.mock.calls.length - 1][0] as { file: FileUpload }
const latestFeatures = setFeaturesMock.mock.calls[setFeaturesMock.mock.calls.length - 1]![0] as { file: FileUpload }
return latestFeatures.file
}
@ -98,8 +98,8 @@ describe('ConfigVision', () => {
it('should show the toggle and parameter controls when visible', () => {
render(<ConfigVision />)
expect(screen.getByText('appDebug.vision.name')).toBeInTheDocument()
expect(screen.getByRole('switch')).toHaveAttribute('aria-checked', 'false')
expect(screen.getByText('appDebug.vision.name'))!.toBeInTheDocument()
expect(screen.getByRole('switch'))!.toHaveAttribute('aria-checked', 'false')
})
it('should enable both image and video uploads when toggled on with video support', async () => {
@ -178,7 +178,7 @@ describe('ParamConfig', () => {
await user.click(screen.getByRole('button', { name: 'appDebug.voice.settings' }))
expect(await screen.findByText('appDebug.vision.visionSettings.title')).toBeInTheDocument()
expect(await screen.findByText('appDebug.vision.visionSettings.title'))!.toBeInTheDocument()
})
})

View File

@ -46,7 +46,8 @@ describe('AssistantTypePicker', () => {
renderComponent()
// Assert
expect(screen.getByText(/chatAssistant.name/i)).toBeInTheDocument()
// Assert
expect(screen.getByText(/chatAssistant.name/i))!.toBeInTheDocument()
})
it('should render chat assistant by default when value is "chat"', () => {
@ -54,7 +55,8 @@ describe('AssistantTypePicker', () => {
renderComponent({ value: 'chat' })
// Assert
expect(screen.getByText(/chatAssistant.name/i)).toBeInTheDocument()
// Assert
expect(screen.getByText(/chatAssistant.name/i))!.toBeInTheDocument()
})
it('should render agent assistant when value is "agent"', () => {
@ -62,7 +64,8 @@ describe('AssistantTypePicker', () => {
renderComponent({ value: 'agent' })
// Assert
expect(screen.getByText(/agentAssistant.name/i)).toBeInTheDocument()
// Assert
expect(screen.getByText(/agentAssistant.name/i))!.toBeInTheDocument()
})
})
@ -73,7 +76,8 @@ describe('AssistantTypePicker', () => {
renderComponent({ value: 'agent' })
// Assert
expect(screen.getByText(/agentAssistant.name/i)).toBeInTheDocument()
// Assert
expect(screen.getByText(/agentAssistant.name/i))!.toBeInTheDocument()
})
it('should handle agentConfig prop', () => {
@ -91,7 +95,8 @@ describe('AssistantTypePicker', () => {
}).not.toThrow()
// Assert
expect(screen.getByText(/chatAssistant.name/i)).toBeInTheDocument()
// Assert
expect(screen.getByText(/chatAssistant.name/i))!.toBeInTheDocument()
})
it('should handle undefined agentConfig prop', () => {
@ -101,7 +106,8 @@ describe('AssistantTypePicker', () => {
}).not.toThrow()
// Assert
expect(screen.getByText(/chatAssistant.name/i)).toBeInTheDocument()
// Assert
expect(screen.getByText(/chatAssistant.name/i))!.toBeInTheDocument()
})
})
@ -137,7 +143,7 @@ describe('AssistantTypePicker', () => {
// Wait for dropdown to open and find chat option
await waitFor(() => {
expect(screen.getByText(/chatAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/chatAssistant.description/i))!.toBeInTheDocument()
})
// Find and click the chat option by its unique description
@ -160,7 +166,7 @@ describe('AssistantTypePicker', () => {
// Wait for dropdown to open and click agent option
await waitFor(() => {
expect(screen.getByText(/agentAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i))!.toBeInTheDocument()
})
const agentOption = getOptionByDescription(/agentAssistant.description/i)
@ -181,7 +187,7 @@ describe('AssistantTypePicker', () => {
// Wait for dropdown and select chat
await waitFor(() => {
expect(screen.getByText(/chatAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/chatAssistant.description/i))!.toBeInTheDocument()
})
const chatOption = getOptionByDescription(/chatAssistant.description/i)
@ -209,11 +215,11 @@ describe('AssistantTypePicker', () => {
})
const agentOptions = screen.getAllByText(/agentAssistant.name/i)
await user.click(agentOptions[0])
await user.click(agentOptions[0]!)
// Assert - Dropdown should remain open (agent settings should be visible)
await waitFor(() => {
expect(screen.getByText(/agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/agent.setting.name/i))!.toBeInTheDocument()
})
})
@ -234,7 +240,7 @@ describe('AssistantTypePicker', () => {
})
const chatOptions = screen.getAllByText(/chatAssistant.name/i)
await user.click(chatOptions[1])
await user.click(chatOptions[1]!)
// Assert
expect(onChange).not.toHaveBeenCalled()
@ -255,7 +261,7 @@ describe('AssistantTypePicker', () => {
// Wait for dropdown to open
await waitFor(() => {
expect(screen.getByText(/agentAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i))!.toBeInTheDocument()
})
// Act - Try to click an option
@ -292,7 +298,7 @@ describe('AssistantTypePicker', () => {
// Assert - Agent settings option should be visible
await waitFor(() => {
expect(screen.getByText(/agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/agent.setting.name/i))!.toBeInTheDocument()
})
})
})
@ -310,7 +316,7 @@ describe('AssistantTypePicker', () => {
// Click agent settings
await waitFor(() => {
expect(screen.getByText(/agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/agent.setting.name/i))!.toBeInTheDocument()
})
const agentSettingsTrigger = screen.getByText(/agent.setting.name/i)
@ -318,7 +324,7 @@ describe('AssistantTypePicker', () => {
// Assert
await waitFor(() => {
expect(screen.getByText(/common.operation.save/i)).toBeInTheDocument()
expect(screen.getByText(/common.operation.save/i))!.toBeInTheDocument()
})
})
@ -333,9 +339,40 @@ describe('AssistantTypePicker', () => {
// Wait for dropdown to open
await waitFor(() => {
expect(screen.getByText(/chatAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/chatAssistant.description/i))!.toBeInTheDocument()
})
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
// Assert - Agent settings modal should not appear (value is 'chat')
expect(screen.queryByText(/common.operation.save/i)).not.toBeInTheDocument()
})
@ -351,7 +388,7 @@ describe('AssistantTypePicker', () => {
await user.click(trigger)
await waitFor(() => {
expect(screen.getByText(/agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/agent.setting.name/i))!.toBeInTheDocument()
})
const agentSettingsTrigger = screen.getByText(/agent.setting.name/i)
@ -359,7 +396,7 @@ describe('AssistantTypePicker', () => {
// Wait for modal and click save
await waitFor(() => {
expect(screen.getByText(/common.operation.save/i)).toBeInTheDocument()
expect(screen.getByText(/common.operation.save/i))!.toBeInTheDocument()
})
const saveButton = screen.getByText(/common.operation.save/i)
@ -379,14 +416,14 @@ describe('AssistantTypePicker', () => {
await user.click(trigger)
await waitFor(() => {
expect(screen.getByText(/agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/agent.setting.name/i))!.toBeInTheDocument()
})
const agentSettingsTrigger = screen.getByText(/agent.setting.name/i)
await user.click(agentSettingsTrigger)
await waitFor(() => {
expect(screen.getByText(/appDebug.agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/appDebug.agent.setting.name/i))!.toBeInTheDocument()
})
const saveButton = screen.getByText(/common.operation.save/i)
@ -409,14 +446,14 @@ describe('AssistantTypePicker', () => {
await user.click(trigger)
await waitFor(() => {
expect(screen.getByText(/agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/agent.setting.name/i))!.toBeInTheDocument()
})
const agentSettingsTrigger = screen.getByText(/agent.setting.name/i)
await user.click(agentSettingsTrigger)
await waitFor(() => {
expect(screen.getByText(/common.operation.save/i)).toBeInTheDocument()
expect(screen.getByText(/common.operation.save/i))!.toBeInTheDocument()
})
const cancelButton = screen.getByText(/common.operation.cancel/i)
@ -439,7 +476,7 @@ describe('AssistantTypePicker', () => {
await user.click(trigger)
await waitFor(() => {
expect(screen.getByText(/agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/agent.setting.name/i))!.toBeInTheDocument()
})
const agentSettingsTrigger = screen.getByText(/agent.setting.name/i)
@ -447,7 +484,7 @@ describe('AssistantTypePicker', () => {
// Assert - Modal should be open and dropdown should close
await waitFor(() => {
expect(screen.getByText(/common.operation.save/i)).toBeInTheDocument()
expect(screen.getByText(/common.operation.save/i))!.toBeInTheDocument()
})
// The dropdown should be closed (agent settings description should not be visible)
@ -472,7 +509,8 @@ describe('AssistantTypePicker', () => {
await user.click(trigger)
// Assert - Should not crash
expect(trigger).toBeInTheDocument()
// Assert - Should not crash
expect(trigger)!.toBeInTheDocument()
})
it('should handle multiple rapid selection changes', async () => {
@ -486,7 +524,7 @@ describe('AssistantTypePicker', () => {
await user.click(trigger)
await waitFor(() => {
expect(screen.getByText(/agentAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i))!.toBeInTheDocument()
})
// Click agent option - this stays open because value is 'agent'
@ -523,7 +561,8 @@ describe('AssistantTypePicker', () => {
}).not.toThrow()
// Assert
expect(screen.getByText(/chatAssistant.name/i)).toBeInTheDocument()
// Assert
expect(screen.getByText(/chatAssistant.name/i))!.toBeInTheDocument()
})
describe('should render with different prop combinations', () => {
@ -542,7 +581,7 @@ describe('AssistantTypePicker', () => {
// Assert
const expectedText = combo.value === 'agent' ? 'agentAssistant.name' : 'chatAssistant.name'
expect(screen.getByText(new RegExp(expectedText, 'i'))).toBeInTheDocument()
expect(screen.getByText(new RegExp(expectedText, 'i')))!.toBeInTheDocument()
},
)
})
@ -561,15 +600,15 @@ describe('AssistantTypePicker', () => {
// Assert - Both options should be visible and clickable
await waitFor(() => {
expect(screen.getByText(/chatAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/chatAssistant.description/i))!.toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i))!.toBeInTheDocument()
})
// Verify we can interact with option elements using helper function
const chatOption = getOptionByDescription(/chatAssistant.description/i)
const agentOption = getOptionByDescription(/agentAssistant.description/i)
expect(chatOption).toBeInTheDocument()
expect(agentOption).toBeInTheDocument()
expect(chatOption)!.toBeInTheDocument()
expect(agentOption)!.toBeInTheDocument()
})
})
@ -586,16 +625,16 @@ describe('AssistantTypePicker', () => {
// Assert - Both options should be visible with radio components
await waitFor(() => {
expect(screen.getByText(/chatAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/chatAssistant.description/i))!.toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i))!.toBeInTheDocument()
})
// The SelectItem components render with different visual states
// based on isChecked prop - we verify both options are rendered
const chatOption = getOptionByDescription(/chatAssistant.description/i)
const agentOption = getOptionByDescription(/agentAssistant.description/i)
expect(chatOption).toBeInTheDocument()
expect(agentOption).toBeInTheDocument()
expect(chatOption)!.toBeInTheDocument()
expect(agentOption)!.toBeInTheDocument()
})
it('should render description text', async () => {
@ -609,8 +648,8 @@ describe('AssistantTypePicker', () => {
// Assert - Descriptions should be visible
await waitFor(() => {
expect(screen.getByText(/chatAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/chatAssistant.description/i))!.toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i))!.toBeInTheDocument()
})
})
@ -625,8 +664,8 @@ describe('AssistantTypePicker', () => {
// Assert - Radio components should be present (both options visible)
await waitFor(() => {
expect(screen.getByText(/chatAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/chatAssistant.description/i))!.toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i))!.toBeInTheDocument()
})
})
})
@ -643,7 +682,7 @@ describe('AssistantTypePicker', () => {
await user.click(trigger)
await waitFor(() => {
expect(screen.getByText(/agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/agent.setting.name/i))!.toBeInTheDocument()
})
const agentSettingsTrigger = screen.getByText(/agent.setting.name/i)
@ -651,9 +690,9 @@ describe('AssistantTypePicker', () => {
// Assert
await waitFor(() => {
expect(screen.getByText(/common.operation.save/i)).toBeInTheDocument()
expect(screen.getByText(/common.operation.save/i))!.toBeInTheDocument()
})
expect(screen.getByText(/appDebug.agent.agentModeType.functionCall/i)).toBeInTheDocument()
expect(screen.getByText(/appDebug.agent.agentModeType.functionCall/i))!.toBeInTheDocument()
})
it('should show built-in prompt when isFunctionCall is false', async () => {
@ -666,7 +705,7 @@ describe('AssistantTypePicker', () => {
await user.click(trigger)
await waitFor(() => {
expect(screen.getByText(/agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/agent.setting.name/i))!.toBeInTheDocument()
})
const agentSettingsTrigger = screen.getByText(/agent.setting.name/i)
@ -674,9 +713,9 @@ describe('AssistantTypePicker', () => {
// Assert
await waitFor(() => {
expect(screen.getByText(/common.operation.save/i)).toBeInTheDocument()
expect(screen.getByText(/common.operation.save/i))!.toBeInTheDocument()
})
expect(screen.getByText(/tools.builtInPromptTitle/i)).toBeInTheDocument()
expect(screen.getByText(/tools.builtInPromptTitle/i))!.toBeInTheDocument()
})
it('should initialize max iteration from agentConfig payload', async () => {
@ -696,7 +735,7 @@ describe('AssistantTypePicker', () => {
await user.click(trigger)
await waitFor(() => {
expect(screen.getByText(/agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/agent.setting.name/i))!.toBeInTheDocument()
})
const agentSettingsTrigger = screen.getByText(/agent.setting.name/i)
@ -705,7 +744,7 @@ describe('AssistantTypePicker', () => {
// Assert
await screen.findByText(/common.operation.save/i)
const maxIterationInput = await screen.findByRole('spinbutton')
expect(maxIterationInput).toHaveValue(10)
expect(maxIterationInput)!.toHaveValue(10)
})
})
@ -721,7 +760,7 @@ describe('AssistantTypePicker', () => {
await user.click(trigger)
await waitFor(() => {
expect(screen.getByText(/chatAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/chatAssistant.description/i))!.toBeInTheDocument()
})
// Press Escape
@ -741,8 +780,9 @@ describe('AssistantTypePicker', () => {
const trigger = screen.getByText(/chatAssistant.name/i)
// Assert - Element should be focusable
expect(trigger).toBeInTheDocument()
expect(trigger.parentElement).toBeInTheDocument()
// Assert - Element should be focusable
expect(trigger)!.toBeInTheDocument()
expect(trigger.parentElement)!.toBeInTheDocument()
})
it('should allow keyboard focus on dropdown options', async () => {
@ -755,7 +795,7 @@ describe('AssistantTypePicker', () => {
await user.click(trigger)
await waitFor(() => {
expect(screen.getByText(/chatAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/chatAssistant.description/i))!.toBeInTheDocument()
})
// Get options
@ -763,8 +803,9 @@ describe('AssistantTypePicker', () => {
const agentOption = getOptionByDescription(/agentAssistant.description/i)
// Assert - Options should be focusable
expect(chatOption).toBeInTheDocument()
expect(agentOption).toBeInTheDocument()
// Assert - Options should be focusable
expect(chatOption)!.toBeInTheDocument()
expect(agentOption)!.toBeInTheDocument()
// Verify options exist and can receive focus programmatically
// Note: focus() doesn't always update document.activeElement in JSDOM
@ -787,11 +828,11 @@ describe('AssistantTypePicker', () => {
// Assert - Agent settings button should be focusable
await waitFor(() => {
expect(screen.getByText(/agent.setting.name/i)).toBeInTheDocument()
expect(screen.getByText(/agent.setting.name/i))!.toBeInTheDocument()
})
const agentSettings = screen.getByText(/agent.setting.name/i)
expect(agentSettings).toBeInTheDocument()
expect(agentSettings)!.toBeInTheDocument()
})
})
@ -804,7 +845,7 @@ describe('AssistantTypePicker', () => {
// Act - Check initial state
const portalContainer = container.querySelector('[data-state]')
expect(portalContainer).toHaveAttribute('data-state', 'closed')
expect(portalContainer)!.toHaveAttribute('data-state', 'closed')
// Open dropdown
const trigger = screen.getByText(/chatAssistant.name/i)
@ -813,7 +854,7 @@ describe('AssistantTypePicker', () => {
// Assert - State should change to open
await waitFor(() => {
const openPortal = container.querySelector('[data-state="open"]')
expect(openPortal).toBeInTheDocument()
expect(openPortal)!.toBeInTheDocument()
})
})
@ -823,11 +864,12 @@ describe('AssistantTypePicker', () => {
// Assert - Portal should have data-state for accessibility
const portalContainer = container.querySelector('[data-state]')
expect(portalContainer).toBeInTheDocument()
expect(portalContainer).toHaveAttribute('data-state')
expect(portalContainer)!.toBeInTheDocument()
expect(portalContainer)!.toHaveAttribute('data-state')
// Should start in closed state
expect(portalContainer).toHaveAttribute('data-state', 'closed')
// Should start in closed state
expect(portalContainer)!.toHaveAttribute('data-state', 'closed')
})
it('should maintain accessible structure for screen readers', () => {
@ -835,7 +877,8 @@ describe('AssistantTypePicker', () => {
renderComponent({ value: 'chat' })
// Assert - Text content should be accessible
expect(screen.getByText(/chatAssistant.name/i)).toBeInTheDocument()
// Assert - Text content should be accessible
expect(screen.getByText(/chatAssistant.name/i))!.toBeInTheDocument()
// Icons should have proper structure
const { container } = renderComponent()
@ -854,12 +897,13 @@ describe('AssistantTypePicker', () => {
// Assert - All options should have descriptive text
await waitFor(() => {
expect(screen.getByText(/chatAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i)).toBeInTheDocument()
expect(screen.getByText(/chatAssistant.description/i))!.toBeInTheDocument()
expect(screen.getByText(/agentAssistant.description/i))!.toBeInTheDocument()
})
// Title text should be visible
expect(screen.getByText(/assistantType.name/i)).toBeInTheDocument()
// Title text should be visible
expect(screen.getByText(/assistantType.name/i))!.toBeInTheDocument()
})
})
})

View File

@ -81,11 +81,11 @@ describe('Result', () => {
/>,
)
expect(screen.getByTestId('prompt-toast')).toHaveTextContent('optimization note')
expect(screen.getByTestId('prompt-res')).toHaveTextContent('generated output')
expect(screen.getByTestId('prompt-toast'))!.toHaveTextContent('optimization note')
expect(screen.getByTestId('prompt-res'))!.toHaveTextContent('generated output')
fireEvent.click(screen.getByTestId('version-selector'))
fireEvent.click(screen.getAllByRole('button')[1])
fireEvent.click(screen.getAllByRole('button')[1]!)
fireEvent.click(screen.getByText('generate.apply'))
expect(mockSetCurrentVersionIndex).toHaveBeenCalledWith(1)
@ -106,7 +106,7 @@ describe('Result', () => {
/>,
)
expect(screen.getByTestId('prompt-res-in-workflow')).toHaveTextContent('generated output')
expect(screen.getByTestId('prompt-res-in-workflow'))!.toHaveTextContent('generated output')
expect(mockPromptResInWorkflow).toHaveBeenCalledWith(expect.objectContaining({
nodeId: 'node-1',
value: 'generated output',
@ -121,6 +121,6 @@ describe('Result', () => {
/>,
)
expect(screen.getByTestId('code-editor')).toHaveTextContent('generated output')
expect(screen.getByTestId('code-editor'))!.toHaveTextContent('generated output')
})
})

View File

@ -61,7 +61,7 @@ vi.mock('es-toolkit/compat', () => ({
return []
// Start with first array and filter down
return validArrays[0].filter((item: any) => {
return validArrays[0]!.filter((item: any) => {
if (!item || !item.name)
return false
@ -317,14 +317,14 @@ describe('DatasetConfig', () => {
it('should render dataset configuration panel when component mounts', () => {
renderDatasetConfig()
expect(screen.getByText('appDebug.feature.dataSet.title')).toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.title'))!.toBeInTheDocument()
})
it('should display empty state message when no datasets are configured', () => {
renderDatasetConfig()
expect(screen.getByText(/no.*data/i)).toBeInTheDocument()
expect(screen.getByTestId('params-config')).toBeDisabled()
expect(screen.getByText(/no.*data/i))!.toBeInTheDocument()
expect(screen.getByTestId('params-config'))!.toBeDisabled()
})
it('should render dataset cards and enable parameters when datasets exist', () => {
@ -333,16 +333,16 @@ describe('DatasetConfig', () => {
dataSets: [dataset],
})
expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
expect(screen.getByText(dataset.name)).toBeInTheDocument()
expect(screen.getByTestId(`card-item-${dataset.id}`))!.toBeInTheDocument()
expect(screen.getByText(dataset.name))!.toBeInTheDocument()
expect(screen.getByTestId('params-config')).not.toBeDisabled()
})
it('should show configuration title and add dataset button in header', () => {
renderDatasetConfig()
expect(screen.getByText('appDebug.feature.dataSet.title')).toBeInTheDocument()
expect(screen.getByText('common.operation.add')).toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.title'))!.toBeInTheDocument()
expect(screen.getByText('common.operation.add'))!.toBeInTheDocument()
})
it('should hide parameters configuration when in agent mode', () => {
@ -436,7 +436,7 @@ describe('DatasetConfig', () => {
dataSets: [dataset],
})
expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
expect(screen.getByTestId(`card-item-${dataset.id}`))!.toBeInTheDocument()
})
})
@ -456,9 +456,10 @@ describe('DatasetConfig', () => {
},
})
expect(screen.getByTestId('context-var')).toBeInTheDocument()
expect(screen.getByTestId('context-var'))!.toBeInTheDocument()
// Should find the selected context variable in the options
expect(screen.getByText('Select context variable')).toBeInTheDocument()
// Should find the selected context variable in the options
expect(screen.getByText('Select context variable'))!.toBeInTheDocument()
})
it('should not show context variable selector in chat mode', () => {
@ -514,8 +515,8 @@ describe('DatasetConfig', () => {
dataSets: [dataset],
})
expect(screen.getByTestId('metadata-filter')).toBeInTheDocument()
expect(screen.getByTestId('metadata-list-count')).toHaveTextContent('2') // both 'category' and 'priority'
expect(screen.getByTestId('metadata-filter'))!.toBeInTheDocument()
expect(screen.getByTestId('metadata-list-count'))!.toHaveTextContent('2') // both 'category' and 'priority'
})
it('should handle metadata filter mode change', async () => {
@ -740,8 +741,8 @@ describe('DatasetConfig', () => {
dataSets: [dataset],
})
expect(screen.getByTestId('metadata-filter')).toBeInTheDocument()
expect(screen.getByTestId('metadata-list-count')).toHaveTextContent('0')
expect(screen.getByTestId('metadata-filter'))!.toBeInTheDocument()
expect(screen.getByTestId('metadata-list-count'))!.toHaveTextContent('0')
})
it('should handle empty doc_metadata array', () => {
@ -753,8 +754,8 @@ describe('DatasetConfig', () => {
dataSets: [dataset],
})
expect(screen.getByTestId('metadata-filter')).toBeInTheDocument()
expect(screen.getByTestId('metadata-list-count')).toHaveTextContent('0')
expect(screen.getByTestId('metadata-filter'))!.toBeInTheDocument()
expect(screen.getByTestId('metadata-list-count'))!.toHaveTextContent('0')
})
it('should handle missing userProfile', () => {
@ -769,7 +770,7 @@ describe('DatasetConfig', () => {
dataSets: [dataset],
})
expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
expect(screen.getByTestId(`card-item-${dataset.id}`))!.toBeInTheDocument()
})
it('should handle missing datasetConfigsRef gracefully', () => {
@ -816,10 +817,10 @@ describe('DatasetConfig', () => {
dataSets: datasets,
})
expect(screen.getByTestId('card-item-ds1')).toBeInTheDocument()
expect(screen.getByTestId('card-item-ds2')).toBeInTheDocument()
expect(screen.getByText('Dataset 1')).toBeInTheDocument()
expect(screen.getByText('Dataset 2')).toBeInTheDocument()
expect(screen.getByTestId('card-item-ds1'))!.toBeInTheDocument()
expect(screen.getByTestId('card-item-ds2'))!.toBeInTheDocument()
expect(screen.getByText('Dataset 1'))!.toBeInTheDocument()
expect(screen.getByText('Dataset 2'))!.toBeInTheDocument()
})
it('should integrate with params config component', () => {
@ -833,8 +834,8 @@ describe('DatasetConfig', () => {
})
const paramsConfig = screen.getByTestId('params-config')
expect(paramsConfig).toBeInTheDocument()
expect(paramsConfig).toHaveTextContent('Params (2)')
expect(paramsConfig)!.toBeInTheDocument()
expect(paramsConfig)!.toHaveTextContent('Params (2)')
expect(paramsConfig).not.toBeDisabled()
})
@ -860,9 +861,10 @@ describe('DatasetConfig', () => {
})
const metadataFilter = screen.getByTestId('metadata-filter')
expect(metadataFilter).toBeInTheDocument()
expect(metadataFilter)!.toBeInTheDocument()
// Should show intersection (only 'category')
expect(screen.getByTestId('metadata-list-count')).toHaveTextContent('1')
// Should show intersection (only 'category')
expect(screen.getByTestId('metadata-list-count'))!.toHaveTextContent('1')
})
})
@ -884,7 +886,7 @@ describe('DatasetConfig', () => {
})
const metadataFilter = screen.getByTestId('metadata-filter')
expect(metadataFilter).toBeInTheDocument()
expect(metadataFilter)!.toBeInTheDocument()
fireEvent.click(within(metadataFilter).getByText('Change Metadata Model'))
@ -915,7 +917,7 @@ describe('DatasetConfig', () => {
})
const metadataFilter = screen.getByTestId('metadata-filter')
expect(metadataFilter).toBeInTheDocument()
expect(metadataFilter)!.toBeInTheDocument()
fireEvent.click(within(metadataFilter).getByText('Change Metadata Params'))
@ -941,7 +943,8 @@ describe('DatasetConfig', () => {
})
// The editable property should be false when no permission
expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
// The editable property should be false when no permission
expect(screen.getByTestId(`card-item-${dataset.id}`))!.toBeInTheDocument()
})
it('should show readonly state for non-editable datasets', () => {
@ -956,7 +959,7 @@ describe('DatasetConfig', () => {
dataSets: [dataset],
})
expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
expect(screen.getByTestId(`card-item-${dataset.id}`))!.toBeInTheDocument()
})
it('should allow editing when user has partial member permission', () => {
@ -972,7 +975,7 @@ describe('DatasetConfig', () => {
dataSets: [dataset],
})
expect(screen.getByTestId(`card-item-${dataset.id}`)).toBeInTheDocument()
expect(screen.getByTestId(`card-item-${dataset.id}`))!.toBeInTheDocument()
})
})
@ -989,9 +992,10 @@ describe('DatasetConfig', () => {
})
// Verify order is maintained
expect(screen.getByText('Dataset 1')).toBeInTheDocument()
expect(screen.getByText('Dataset 2')).toBeInTheDocument()
expect(screen.getByText('Dataset 3')).toBeInTheDocument()
// Verify order is maintained
expect(screen.getByText('Dataset 1'))!.toBeInTheDocument()
expect(screen.getByText('Dataset 2'))!.toBeInTheDocument()
expect(screen.getByText('Dataset 3'))!.toBeInTheDocument()
})
it('should handle multiple dataset operations correctly', async () => {
@ -1007,7 +1011,7 @@ describe('DatasetConfig', () => {
// Remove first dataset
const removeButton1 = screen.getAllByText('Remove')[0]
await user.click(removeButton1)
await user.click(removeButton1!)
expect(mockConfigContext.setDataSets).toHaveBeenCalledWith([datasets[1]])
})
@ -1050,7 +1054,7 @@ describe('DatasetConfig', () => {
dataSets: datasets,
})
expect(screen.getByTestId('params-config')).toHaveTextContent('Params (2)')
expect(screen.getByTestId('params-config'))!.toHaveTextContent('Params (2)')
})
it('should handle external knowledge base integration', () => {
@ -1068,8 +1072,8 @@ describe('DatasetConfig', () => {
dataSets: [externalDataset],
})
expect(screen.getByTestId(`card-item-${externalDataset.id}`)).toBeInTheDocument()
expect(screen.getByText(externalDataset.name)).toBeInTheDocument()
expect(screen.getByTestId(`card-item-${externalDataset.id}`))!.toBeInTheDocument()
expect(screen.getByText(externalDataset.name))!.toBeInTheDocument()
})
})
@ -1090,7 +1094,7 @@ describe('DatasetConfig', () => {
dataSets: manyDatasets,
})
expect(screen.getByTestId('params-config')).toHaveTextContent('Params (50)')
expect(screen.getByTestId('params-config'))!.toHaveTextContent('Params (50)')
})
it('should handle metadata intersection calculation efficiently', () => {
@ -1118,7 +1122,8 @@ describe('DatasetConfig', () => {
})
// Should calculate intersection correctly
expect(screen.getByTestId('metadata-filter')).toBeInTheDocument()
// Should calculate intersection correctly
expect(screen.getByTestId('metadata-filter'))!.toBeInTheDocument()
})
})
})

View File

@ -157,9 +157,9 @@ describe('dataset-config/card-item', () => {
const card = screen.getByText(dataset.name).closest('.group') as HTMLElement
const actionButtons = within(card).getAllByRole('button', { hidden: true })
expect(screen.getByText(dataset.name)).toBeInTheDocument()
expect(screen.getByText('dataset.indexingTechnique.high_quality · dataset.indexingMethod.semantic_search')).toBeInTheDocument()
expect(screen.getByText('dataset.externalTag')).toBeInTheDocument()
expect(screen.getByText(dataset.name))!.toBeInTheDocument()
expect(screen.getByText('dataset.indexingTechnique.high_quality · dataset.indexingMethod.semantic_search'))!.toBeInTheDocument()
expect(screen.getByText('dataset.externalTag'))!.toBeInTheDocument()
expect(actionButtons).toHaveLength(2)
})
@ -170,9 +170,9 @@ describe('dataset-config/card-item', () => {
const card = screen.getByText(dataset.name).closest('.group') as HTMLElement
const [editButton] = within(card).getAllByRole('button', { hidden: true })
await user.click(editButton)
await user.click(editButton!)
expect(await screen.findByText('Mock settings modal')).toBeInTheDocument()
expect(await screen.findByText('Mock settings modal'))!.toBeInTheDocument()
fireEvent.click(await screen.findByText('Save changes'))
await waitFor(() => {
@ -213,8 +213,8 @@ describe('dataset-config/card-item', () => {
const nameElement = screen.getByText(dataset.name)
const iconElement = nameElement.parentElement?.firstElementChild as HTMLElement
expect(iconElement).toHaveStyle({ background: '#FFF4ED' })
expect(iconElement.querySelector('em-emoji')).toHaveAttribute('id', '📙')
expect(iconElement)!.toHaveStyle({ background: '#FFF4ED' })
expect(iconElement.querySelector('em-emoji'))!.toHaveAttribute('id', '📙')
})
it('should apply mask overlay on mobile when drawer is open', async () => {
@ -226,12 +226,12 @@ describe('dataset-config/card-item', () => {
const card = screen.getByText(dataset.name).closest('.group') as HTMLElement
const [editButton] = within(card).getAllByRole('button', { hidden: true })
await user.click(editButton)
expect(screen.getByText('Mock settings modal')).toBeInTheDocument()
await user.click(editButton!)
expect(screen.getByText('Mock settings modal'))!.toBeInTheDocument()
const overlay = [...document.querySelectorAll('[class]')]
.find(element => element.className.toString().includes('bg-black/30'))
expect(overlay).toBeInTheDocument()
expect(overlay)!.toBeInTheDocument()
})
})

View File

@ -87,7 +87,8 @@ describe('ContextVar', () => {
render(<ContextVar {...props} />)
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.title')).toBeInTheDocument()
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.title'))!.toBeInTheDocument()
})
it('should show selected variable with proper formatting when value is provided', () => {
@ -98,9 +99,10 @@ describe('ContextVar', () => {
render(<ContextVar {...props} />)
// Assert
expect(screen.getByText('var1')).toBeInTheDocument()
expect(screen.getByText('{{')).toBeInTheDocument()
expect(screen.getByText('}}')).toBeInTheDocument()
// Assert
expect(screen.getByText('var1'))!.toBeInTheDocument()
expect(screen.getByText('{{'))!.toBeInTheDocument()
expect(screen.getByText('}}'))!.toBeInTheDocument()
})
})
@ -114,7 +116,8 @@ describe('ContextVar', () => {
render(<ContextVar {...props} />)
// Assert - Should display the selected value
expect(screen.getByText('var2')).toBeInTheDocument()
// Assert - Should display the selected value
expect(screen.getByText('var2'))!.toBeInTheDocument()
})
it('should show placeholder text when no value is selected', () => {
@ -127,9 +130,40 @@ describe('ContextVar', () => {
// Act
render(<ContextVar {...props} />)
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
// Assert - Should show placeholder instead of variable
expect(screen.queryByText('var1')).not.toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder')).toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder'))!.toBeInTheDocument()
})
it('should display custom tip message when notSelectedVarTip is provided', () => {
@ -144,7 +178,8 @@ describe('ContextVar', () => {
render(<ContextVar {...props} />)
// Assert
expect(screen.getByText('Select a variable')).toBeInTheDocument()
// Assert
expect(screen.getByText('Select a variable'))!.toBeInTheDocument()
})
it('should apply custom className to VarPicker when provided', () => {
@ -158,7 +193,8 @@ describe('ContextVar', () => {
const { container } = render(<ContextVar {...props} />)
// Assert
expect(container.querySelector('.custom-class')).toBeInTheDocument()
// Assert
expect(container.querySelector('.custom-class'))!.toBeInTheDocument()
})
})
@ -176,13 +212,13 @@ describe('ContextVar', () => {
const triggers = screen.getAllByTestId('portal-trigger')
const varPickerTrigger = triggers[triggers.length - 1]
await user.click(varPickerTrigger)
expect(screen.getByTestId('portal-content')).toBeInTheDocument()
await user.click(varPickerTrigger!)
expect(screen.getByTestId('portal-content'))!.toBeInTheDocument()
// Select a different option
const options = screen.getAllByText('var2')
expect(options.length).toBeGreaterThan(0)
await user.click(options[0])
await user.click(options[0]!)
// Assert
expect(onChange).toHaveBeenCalledWith('var2')
@ -201,11 +237,11 @@ describe('ContextVar', () => {
const varPickerTrigger = triggers[triggers.length - 1]
// Open dropdown
await user.click(varPickerTrigger)
expect(screen.getByTestId('portal-content')).toBeInTheDocument()
await user.click(varPickerTrigger!)
expect(screen.getByTestId('portal-content'))!.toBeInTheDocument()
// Close dropdown
await user.click(varPickerTrigger)
await user.click(varPickerTrigger!)
expect(screen.queryByTestId('portal-content')).not.toBeInTheDocument()
})
})
@ -223,8 +259,9 @@ describe('ContextVar', () => {
render(<ContextVar {...props} />)
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.title')).toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder')).toBeInTheDocument()
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.title'))!.toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder'))!.toBeInTheDocument()
expect(screen.queryByText('var1')).not.toBeInTheDocument()
})
@ -240,8 +277,9 @@ describe('ContextVar', () => {
render(<ContextVar {...props} />)
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.title')).toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder')).toBeInTheDocument()
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.title'))!.toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder'))!.toBeInTheDocument()
})
it('should handle null value without crashing', () => {
@ -255,8 +293,9 @@ describe('ContextVar', () => {
render(<ContextVar {...props} />)
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.title')).toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder')).toBeInTheDocument()
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.title'))!.toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder'))!.toBeInTheDocument()
})
it('should handle options with different data types', () => {
@ -275,9 +314,10 @@ describe('ContextVar', () => {
render(<ContextVar {...props} />)
// Assert
expect(screen.getByText('strVar')).toBeInTheDocument()
expect(screen.getByText('{{')).toBeInTheDocument()
expect(screen.getByText('}}')).toBeInTheDocument()
// Assert
expect(screen.getByText('strVar'))!.toBeInTheDocument()
expect(screen.getByText('{{'))!.toBeInTheDocument()
expect(screen.getByText('}}'))!.toBeInTheDocument()
})
it('should render variable names with special characters safely', () => {
@ -294,7 +334,8 @@ describe('ContextVar', () => {
render(<ContextVar {...props} />)
// Assert
expect(screen.getByText('specialVar')).toBeInTheDocument()
// Assert
expect(screen.getByText('specialVar'))!.toBeInTheDocument()
})
})
})

View File

@ -88,8 +88,9 @@ describe('VarPicker', () => {
render(<VarPicker {...props} />)
// Assert
expect(screen.getByTestId('portal-trigger')).toBeInTheDocument()
expect(screen.getByText('var1')).toBeInTheDocument()
// Assert
expect(screen.getByTestId('portal-trigger'))!.toBeInTheDocument()
expect(screen.getByText('var1'))!.toBeInTheDocument()
})
it('should display selected variable with type icon when value is provided', () => {
@ -100,11 +101,13 @@ describe('VarPicker', () => {
render(<VarPicker {...props} />)
// Assert
expect(screen.getByText('var1')).toBeInTheDocument()
expect(screen.getByText('{{')).toBeInTheDocument()
expect(screen.getByText('}}')).toBeInTheDocument()
// Assert
expect(screen.getByText('var1'))!.toBeInTheDocument()
expect(screen.getByText('{{'))!.toBeInTheDocument()
expect(screen.getByText('}}'))!.toBeInTheDocument()
// IconTypeIcon should be rendered (check for svg icon)
expect(document.querySelector('svg')).toBeInTheDocument()
// IconTypeIcon should be rendered (check for svg icon)
expect(document.querySelector('svg'))!.toBeInTheDocument()
})
it('should show placeholder text when no value is selected', () => {
@ -117,9 +120,40 @@ describe('VarPicker', () => {
// Act
render(<VarPicker {...props} />)
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
expect(screen.queryByText('var1')).not.toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder')).toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder'))!.toBeInTheDocument()
})
it('should display custom tip message when notSelectedVarTip is provided', () => {
@ -134,7 +168,8 @@ describe('VarPicker', () => {
render(<VarPicker {...props} />)
// Assert
expect(screen.getByText('Select a variable')).toBeInTheDocument()
// Assert
expect(screen.getByText('Select a variable'))!.toBeInTheDocument()
})
it('should render dropdown indicator icon', () => {
@ -145,7 +180,8 @@ describe('VarPicker', () => {
render(<VarPicker {...props} />)
// Assert - Trigger should be present
expect(screen.getByTestId('portal-trigger')).toBeInTheDocument()
// Assert - Trigger should be present
expect(screen.getByTestId('portal-trigger'))!.toBeInTheDocument()
})
})
@ -162,7 +198,8 @@ describe('VarPicker', () => {
const { container } = render(<VarPicker {...props} />)
// Assert
expect(container.querySelector('.custom-class')).toBeInTheDocument()
// Assert
expect(container.querySelector('.custom-class'))!.toBeInTheDocument()
})
it('should apply custom triggerClassName to trigger button', () => {
@ -176,7 +213,8 @@ describe('VarPicker', () => {
render(<VarPicker {...props} />)
// Assert
expect(screen.getByTestId('portal-trigger')).toHaveClass('custom-trigger-class')
// Assert
expect(screen.getByTestId('portal-trigger'))!.toHaveClass('custom-trigger-class')
})
it('should display selected value with proper formatting', () => {
@ -193,9 +231,10 @@ describe('VarPicker', () => {
render(<VarPicker {...props} />)
// Assert
expect(screen.getByText('customVar')).toBeInTheDocument()
expect(screen.getByText('{{')).toBeInTheDocument()
expect(screen.getByText('}}')).toBeInTheDocument()
// Assert
expect(screen.getByText('customVar'))!.toBeInTheDocument()
expect(screen.getByText('{{'))!.toBeInTheDocument()
expect(screen.getByText('}}'))!.toBeInTheDocument()
})
})
@ -212,7 +251,8 @@ describe('VarPicker', () => {
await user.click(screen.getByTestId('portal-trigger'))
// Assert
expect(screen.getByTestId('portal-content')).toBeInTheDocument()
// Assert
expect(screen.getByTestId('portal-content'))!.toBeInTheDocument()
})
it('should call onChange and close dropdown when selecting an option', async () => {
@ -226,12 +266,12 @@ describe('VarPicker', () => {
// Open dropdown
await user.click(screen.getByTestId('portal-trigger'))
expect(screen.getByTestId('portal-content')).toBeInTheDocument()
expect(screen.getByTestId('portal-content'))!.toBeInTheDocument()
// Select a different option
const options = screen.getAllByText('var2')
expect(options.length).toBeGreaterThan(0)
await user.click(options[0])
await user.click(options[0]!)
// Assert
expect(onChange).toHaveBeenCalledWith('var2')
@ -250,7 +290,7 @@ describe('VarPicker', () => {
// Open dropdown
await user.click(trigger)
expect(screen.getByTestId('portal-content')).toBeInTheDocument()
expect(screen.getByTestId('portal-content'))!.toBeInTheDocument()
// Close dropdown
await user.click(trigger)
@ -267,6 +307,37 @@ describe('VarPicker', () => {
// Act
render(<VarPicker {...props} />)
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
expect(screen.queryByTestId('portal-content')).not.toBeInTheDocument()
})
@ -284,7 +355,7 @@ describe('VarPicker', () => {
// Open dropdown
await user.click(trigger)
expect(screen.getByTestId('portal-content')).toBeInTheDocument()
expect(screen.getByTestId('portal-content'))!.toBeInTheDocument()
// Close dropdown
await user.click(trigger)
@ -305,7 +376,8 @@ describe('VarPicker', () => {
await user.click(trigger)
// Assert
expect(screen.getByText('var1')).toBeInTheDocument() // Original value still displayed
// Assert
expect(screen.getByText('var1'))!.toBeInTheDocument() // Original value still displayed
})
})
@ -322,8 +394,9 @@ describe('VarPicker', () => {
render(<VarPicker {...props} />)
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder')).toBeInTheDocument()
expect(screen.getByTestId('portal-trigger')).toBeInTheDocument()
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder'))!.toBeInTheDocument()
expect(screen.getByTestId('portal-trigger'))!.toBeInTheDocument()
})
it('should handle empty options array', () => {
@ -338,8 +411,9 @@ describe('VarPicker', () => {
render(<VarPicker {...props} />)
// Assert
expect(screen.getByTestId('portal-trigger')).toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder')).toBeInTheDocument()
// Assert
expect(screen.getByTestId('portal-trigger'))!.toBeInTheDocument()
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder'))!.toBeInTheDocument()
})
it('should handle null value without crashing', () => {
@ -353,7 +427,8 @@ describe('VarPicker', () => {
render(<VarPicker {...props} />)
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder')).toBeInTheDocument()
// Assert
expect(screen.getByText('appDebug.feature.dataSet.queryVariable.choosePlaceholder'))!.toBeInTheDocument()
})
it('should handle variable names with special characters safely', () => {
@ -370,7 +445,8 @@ describe('VarPicker', () => {
render(<VarPicker {...props} />)
// Assert
expect(screen.getByText('specialVar')).toBeInTheDocument()
// Assert
expect(screen.getByText('specialVar'))!.toBeInTheDocument()
})
it('should handle long variable names', () => {
@ -387,8 +463,9 @@ describe('VarPicker', () => {
render(<VarPicker {...props} />)
// Assert
expect(screen.getByText('longVar')).toBeInTheDocument()
expect(screen.getByTestId('portal-trigger')).toBeInTheDocument()
// Assert
expect(screen.getByText('longVar'))!.toBeInTheDocument()
expect(screen.getByTestId('portal-trigger'))!.toBeInTheDocument()
})
})
})

View File

@ -203,7 +203,7 @@ describe('ConfigContent', () => {
await waitFor(() => {
expect(onChange).toHaveBeenCalled()
})
const [nextConfigs] = onChange.mock.calls[0]
const [nextConfigs] = (onChange.mock.calls[0] ?? []) as [any]
expect(nextConfigs.retrieval_model).toBe(RETRIEVE_TYPE.multiWay)
})
})
@ -239,10 +239,11 @@ describe('ConfigContent', () => {
)
// Assert
expect(screen.getByText('dataset.weightedScore.title')).toBeInTheDocument()
expect(screen.getByText('common.modelProvider.rerankModel.key')).toBeInTheDocument()
expect(screen.getByText('dataset.weightedScore.semantic')).toBeInTheDocument()
expect(screen.getByText('dataset.weightedScore.keyword')).toBeInTheDocument()
// Assert
expect(screen.getByText('dataset.weightedScore.title'))!.toBeInTheDocument()
expect(screen.getByText('common.modelProvider.rerankModel.key'))!.toBeInTheDocument()
expect(screen.getByText('dataset.weightedScore.semantic'))!.toBeInTheDocument()
expect(screen.getByText('dataset.weightedScore.keyword'))!.toBeInTheDocument()
})
})

View File

@ -164,7 +164,8 @@ describe('dataset-config/params-config', () => {
renderParamsConfig({ disabled: true })
// Assert
expect(screen.getByRole('button', { name: 'dataset.retrievalSettings' })).toBeDisabled()
// Assert
expect(screen.getByRole('button', { name: 'dataset.retrievalSettings' }))!.toBeDisabled()
})
})
@ -181,11 +182,11 @@ describe('dataset-config/params-config', () => {
const dialogScope = within(dialog)
const incrementButtons = dialogScope.getAllByRole('button', { name: /increment/i })
await user.click(incrementButtons[0])
await user.click(incrementButtons[0]!)
await waitFor(() => {
const [topKInput] = dialogScope.getAllByRole('textbox')
expect(topKInput).toHaveValue('5')
expect(topKInput)!.toHaveValue('5')
})
await user.click(dialogScope.getByRole('button', { name: 'common.operation.save' }))
@ -200,7 +201,8 @@ describe('dataset-config/params-config', () => {
const [reopenedTopKInput] = reopenedScope.getAllByRole('textbox')
// Assert
expect(reopenedTopKInput).toHaveValue('5')
// Assert
expect(reopenedTopKInput)!.toHaveValue('5')
})
it('should discard changes when cancel is clicked', async () => {
@ -214,11 +216,11 @@ describe('dataset-config/params-config', () => {
const dialogScope = within(dialog)
const incrementButtons = dialogScope.getAllByRole('button', { name: /increment/i })
await user.click(incrementButtons[0])
await user.click(incrementButtons[0]!)
await waitFor(() => {
const [topKInput] = dialogScope.getAllByRole('textbox')
expect(topKInput).toHaveValue('5')
expect(topKInput)!.toHaveValue('5')
})
const cancelButton = await dialogScope.findByRole('button', { name: 'common.operation.cancel' })
@ -234,7 +236,8 @@ describe('dataset-config/params-config', () => {
const [reopenedTopKInput] = reopenedScope.getAllByRole('textbox')
// Assert
expect(reopenedTopKInput).toHaveValue('4')
// Assert
expect(reopenedTopKInput)!.toHaveValue('4')
})
it('should prevent saving when rerank model is required but invalid', async () => {
@ -255,7 +258,7 @@ describe('dataset-config/params-config', () => {
// Assert
expect(toastErrorSpy).toHaveBeenCalledWith('appDebug.datasetConfig.rerankModelRequired')
expect(screen.getByRole('dialog')).toBeInTheDocument()
expect(screen.getByRole('dialog'))!.toBeInTheDocument()
})
})
})

View File

@ -121,10 +121,10 @@ const ConfigContent: FC<Props> = ({
...datasetConfigs.weights!,
vector_setting: {
...datasetConfigs.weights!.vector_setting!,
vector_weight: value.value[0],
vector_weight: value.value[0]!,
},
keyword_setting: {
keyword_weight: value.value[1],
keyword_weight: value.value[1]!,
},
},
}

View File

@ -54,10 +54,10 @@ const WeightedScore = ({
<div className="mr-1 truncate uppercase" title={t('weightedScore.semantic', { ns: 'dataset' }) || ''}>
{t('weightedScore.semantic', { ns: 'dataset' })}
</div>
{formatNumber(value.value[0])}
{formatNumber(value.value[0]!)}
</div>
<div className="flex w-[90px] shrink-0 items-center justify-end text-util-colors-teal-teal-500 system-xs-semibold-uppercase">
{formatNumber(value.value[1])}
{formatNumber(value.value[1]!)}
<div className="ml-1 truncate uppercase" title={t('weightedScore.keyword', { ns: 'dataset' }) || ''}>
{t('weightedScore.keyword', { ns: 'dataset' })}
</div>

View File

@ -152,7 +152,8 @@ describe('RetrievalChangeTip', () => {
await userEvent.click(screen.getByRole('button', { name: 'close-retrieval-change-tip' }))
// Assert
expect(screen.getByText('Test message')).toBeInTheDocument()
// Assert
expect(screen.getByText('Test message'))!.toBeInTheDocument()
expect(onDismiss).toHaveBeenCalledTimes(1)
})
@ -160,6 +161,37 @@ describe('RetrievalChangeTip', () => {
// Arrange & Act
render(<RetrievalChangeTip {...defaultProps} visible={false} />)
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
expect(screen.queryByText('Test message')).not.toBeInTheDocument()
})
@ -213,12 +245,13 @@ describe('RetrievalSection', () => {
/>,
)
const [topKIncrement] = screen.getAllByRole('button', { name: /increment/i })
await userEvent.click(topKIncrement)
await userEvent.click(topKIncrement!)
// Assert
expect(screen.getByText('External API')).toBeInTheDocument()
expect(screen.getByText('https://api.external.com')).toBeInTheDocument()
expect(screen.getByText('ext-id-999')).toBeInTheDocument()
// Assert
expect(screen.getByText('External API'))!.toBeInTheDocument()
expect(screen.getByText('https://api.external.com'))!.toBeInTheDocument()
expect(screen.getByText('ext-id-999'))!.toBeInTheDocument()
expect(handleExternalChange).toHaveBeenCalledWith(expect.objectContaining({ top_k: 4 }))
})
@ -243,9 +276,10 @@ describe('RetrievalSection', () => {
)
// Assert
expect(screen.getByText('dataset.retrieval.semantic_search.title')).toBeInTheDocument()
// Assert
expect(screen.getByText('dataset.retrieval.semantic_search.title'))!.toBeInTheDocument()
const learnMoreLink = screen.getByRole('link', { name: 'datasetSettings.form.retrievalSetting.learnMore' })
expect(learnMoreLink).toHaveAttribute('href', 'https://docs.example/use-dify/knowledge/create-knowledge/setting-indexing-methods')
expect(learnMoreLink)!.toHaveAttribute('href', 'https://docs.example/use-dify/knowledge/create-knowledge/setting-indexing-methods')
expect(docLink).toHaveBeenCalledWith('/use-dify/knowledge/create-knowledge/setting-indexing-methods')
})
@ -268,10 +302,11 @@ describe('RetrievalSection', () => {
/>,
)
const [topKIncrement] = screen.getAllByRole('button', { name: /increment/i })
await userEvent.click(topKIncrement)
await userEvent.click(topKIncrement!)
// Assert
expect(screen.getByText('dataset.retrieval.keyword_search.title')).toBeInTheDocument()
// Assert
expect(screen.getByText('dataset.retrieval.keyword_search.title'))!.toBeInTheDocument()
expect(handleRetrievalChange).toHaveBeenCalledWith(expect.objectContaining({
top_k: 3,
}))

View File

@ -479,8 +479,8 @@ describe('Debug', () => {
},
})
expect(screen.getByText('appDebug.noModelProviderConfigured')).toBeInTheDocument()
expect(screen.getByText('appDebug.noModelProviderConfiguredTip')).toBeInTheDocument()
expect(screen.getByText('appDebug.noModelProviderConfigured'))!.toBeInTheDocument()
expect(screen.getByText('appDebug.noModelProviderConfiguredTip'))!.toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: 'appDebug.manageModels' }))
expect(onSetting).toHaveBeenCalledTimes(1)
@ -500,8 +500,8 @@ describe('Debug', () => {
},
})
expect(screen.getByText('appDebug.noModelSelected')).toBeInTheDocument()
expect(screen.getByText('appDebug.noModelSelectedTip')).toBeInTheDocument()
expect(screen.getByText('appDebug.noModelSelected'))!.toBeInTheDocument()
expect(screen.getByText('appDebug.noModelSelectedTip'))!.toBeInTheDocument()
expect(screen.queryByText('appDebug.noModelProviderConfigured')).not.toBeInTheDocument()
})
})
@ -510,9 +510,9 @@ describe('Debug', () => {
it('should render single-model panel and refresh conversation', () => {
renderDebug()
expect(screen.getByTestId('debug-with-single-model')).toBeInTheDocument()
expect(screen.getByTestId('debug-with-single-model'))!.toBeInTheDocument()
fireEvent.click(screen.getAllByTestId('action-button')[0])
fireEvent.click(screen.getAllByTestId('action-button')[0]!)
expect(mockState.mockHandleRestart).toHaveBeenCalledTimes(1)
})
@ -535,8 +535,8 @@ describe('Debug', () => {
},
})
expect(screen.getByTestId('chat-user-input')).toBeInTheDocument()
fireEvent.click(screen.getAllByTestId('action-button')[1])
expect(screen.getByTestId('chat-user-input'))!.toBeInTheDocument()
fireEvent.click(screen.getAllByTestId('action-button')[1]!)
expect(screen.queryByTestId('chat-user-input')).not.toBeInTheDocument()
})
@ -560,7 +560,7 @@ describe('Debug', () => {
},
})
expect(screen.getByTestId('formatting-changed')).toBeInTheDocument()
expect(screen.getByTestId('formatting-changed'))!.toBeInTheDocument()
fireEvent.click(screen.getByTestId('formatting-cancel'))
expect(setFormattingChanged).toHaveBeenCalledWith(false)
})
@ -623,8 +623,8 @@ describe('Debug', () => {
},
})
expect(screen.getByTestId('prompt-value-panel')).toBeInTheDocument()
expect(screen.getByText('appDebug.noResult')).toBeInTheDocument()
expect(screen.getByTestId('prompt-value-panel'))!.toBeInTheDocument()
expect(screen.getByText('appDebug.noResult'))!.toBeInTheDocument()
})
it('should notify when required input is missing', () => {
@ -696,7 +696,7 @@ describe('Debug', () => {
})
fireEvent.click(screen.getByTestId('panel-send'))
expect(screen.getByTestId('cannot-query-dataset')).toBeInTheDocument()
expect(screen.getByTestId('cannot-query-dataset'))!.toBeInTheDocument()
fireEvent.click(screen.getByTestId('cannot-query-confirm'))
expect(screen.queryByTestId('cannot-query-dataset')).not.toBeInTheDocument()
@ -752,7 +752,7 @@ describe('Debug', () => {
fireEvent.click(screen.getByTestId('panel-send'))
await waitFor(() => expect(mockState.mockSendCompletionMessage).toHaveBeenCalledTimes(1))
const [, requestData] = mockState.mockSendCompletionMessage.mock.calls[0]
const [, requestData] = (mockState.mockSendCompletionMessage.mock.calls[0] ?? []) as [unknown, any]
expect(requestData).toMatchObject({
inputs: { question: 'hello' },
model_config: {
@ -763,9 +763,9 @@ describe('Debug', () => {
dataset_query_variable: 'question',
},
})
expect(screen.getByTestId('text-generation')).toHaveTextContent('final answer')
expect(screen.getByTestId('text-generation')).toHaveAttribute('data-message-id', 'msg-1')
expect(screen.getByTestId('text-generation')).toHaveAttribute('data-tts', 'true')
expect(screen.getByTestId('text-generation'))!.toHaveTextContent('final answer')
expect(screen.getByTestId('text-generation'))!.toHaveAttribute('data-message-id', 'msg-1')
expect(screen.getByTestId('text-generation'))!.toHaveAttribute('data-tts', 'true')
})
it('should notify when sending again while a response is in progress', async () => {
@ -830,7 +830,7 @@ describe('Debug', () => {
fireEvent.click(screen.getByTestId('panel-send'))
await waitFor(() => expect(mockState.mockSendCompletionMessage).toHaveBeenCalledTimes(1))
expect(screen.getByText('appDebug.noResult')).toBeInTheDocument()
expect(screen.getByText('appDebug.noResult'))!.toBeInTheDocument()
})
it('should render prompt log modal in completion mode when store flag is enabled', () => {
@ -845,7 +845,7 @@ describe('Debug', () => {
},
})
expect(screen.getByTestId('prompt-log-modal')).toBeInTheDocument()
expect(screen.getByTestId('prompt-log-modal'))!.toBeInTheDocument()
})
it('should close prompt log modal in completion mode', () => {
@ -904,7 +904,7 @@ describe('Debug', () => {
},
})
expect(screen.getByRole('button', { name: 'common.modelProvider.addModel(4/4)' })).toBeDisabled()
expect(screen.getByRole('button', { name: 'common.modelProvider.addModel(4/4)' }))!.toBeDisabled()
})
it('should emit completion event in multiple-model completion mode', () => {
@ -945,7 +945,7 @@ describe('Debug', () => {
},
})
fireEvent.click(screen.getAllByTestId('action-button')[0])
fireEvent.click(screen.getAllByTestId('action-button')[0]!)
expect(mockState.mockEventEmitterEmit).toHaveBeenCalledWith({
type: APP_CHAT_WITH_MULTIPLE_MODEL_RESTART,
})
@ -1012,8 +1012,8 @@ describe('Debug', () => {
},
})
expect(screen.getByTestId('prompt-log-modal')).toBeInTheDocument()
expect(screen.getByTestId('agent-log-modal')).toBeInTheDocument()
expect(screen.getByTestId('prompt-log-modal'))!.toBeInTheDocument()
expect(screen.getByTestId('agent-log-modal'))!.toBeInTheDocument()
})
it('should close prompt and agent log modals in multiple-model mode', () => {

View File

@ -8,7 +8,7 @@ const ContextConsumer = () => {
<div>
<div>{value.multipleModelConfigs.length}</div>
<button onClick={() => value.onMultipleModelConfigsChange(true, value.multipleModelConfigs)}>change-multiple</button>
<button onClick={() => value.onDebugWithMultipleModelChange(value.multipleModelConfigs[0])}>change-single</button>
<button onClick={() => value.onDebugWithMultipleModelChange(value.multipleModelConfigs[0]!)}>change-single</button>
<div>{String(value.checkCanSend?.())}</div>
</div>
)
@ -32,7 +32,7 @@ describe('DebugWithMultipleModelContextProvider', () => {
</DebugWithMultipleModelContextProvider>,
)
expect(screen.getByText('1')).toBeInTheDocument()
expect(screen.getByText('true')).toBeInTheDocument()
expect(screen.getByText('1'))!.toBeInTheDocument()
expect(screen.getByText('true'))!.toBeInTheDocument()
})
})

View File

@ -141,8 +141,8 @@ describe('DebugItem', () => {
it('should render with basic props', () => {
renderComponent()
expect(screen.getByTestId('model-parameter-trigger')).toBeInTheDocument()
expect(screen.getByTestId('dropdown')).toBeInTheDocument()
expect(screen.getByTestId('model-parameter-trigger'))!.toBeInTheDocument()
expect(screen.getByTestId('dropdown'))!.toBeInTheDocument()
})
it('should display correct index number', () => {
@ -170,7 +170,7 @@ describe('DebugItem', () => {
})
const wrapper = container.firstChild as HTMLElement
expect(wrapper).toHaveClass('custom-class')
expect(wrapper)!.toHaveClass('custom-class')
expect(wrapper.style.backgroundColor).toBe('red')
})
@ -193,7 +193,7 @@ describe('DebugItem', () => {
renderComponent()
expect(screen.getByTestId('chat-item')).toBeInTheDocument()
expect(screen.getByTestId('chat-item'))!.toBeInTheDocument()
expect(screen.queryByTestId('text-generation-item')).not.toBeInTheDocument()
})
@ -207,7 +207,7 @@ describe('DebugItem', () => {
renderComponent()
expect(screen.getByTestId('chat-item')).toBeInTheDocument()
expect(screen.getByTestId('chat-item'))!.toBeInTheDocument()
})
it('should not render ChatItem when model is not active', () => {
@ -261,7 +261,7 @@ describe('DebugItem', () => {
renderComponent()
expect(screen.getByTestId('text-generation-item')).toBeInTheDocument()
expect(screen.getByTestId('text-generation-item'))!.toBeInTheDocument()
expect(screen.queryByTestId('chat-item')).not.toBeInTheDocument()
})
@ -502,7 +502,7 @@ describe('DebugItem', () => {
expect.arrayContaining([
models[0],
models[1],
expect.objectContaining({ model: models[1].model }),
expect.objectContaining({ model: models[1]!.model }),
models[2],
]),
)
@ -545,6 +545,37 @@ describe('DebugItem', () => {
renderComponent()
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
// When provider/model doesn't match, ChatItem won't render
expect(screen.queryByTestId('chat-item')).not.toBeInTheDocument()
})

View File

@ -194,13 +194,13 @@ describe('DebugWithMultipleModel', () => {
it('should handle empty multipleModelConfigs array', () => {
renderComponent({ multipleModelConfigs: [] })
expect(screen.queryByTestId('debug-item')).not.toBeInTheDocument()
expect(screen.getByTestId('chat-input-area')).toBeInTheDocument()
expect(screen.getByTestId('chat-input-area'))!.toBeInTheDocument()
})
it('should handle model config with missing required fields', () => {
const incompleteConfig = { id: 'incomplete' } as ModelAndParameter
renderComponent({ multipleModelConfigs: [incompleteConfig] })
expect(screen.getByTestId('debug-item')).toBeInTheDocument()
expect(screen.getByTestId('debug-item'))!.toBeInTheDocument()
})
it('should handle more than 4 model configs', () => {
@ -257,7 +257,8 @@ describe('DebugWithMultipleModel', () => {
renderComponent()
// Should still render but handle gracefully
expect(screen.getByTestId('chat-input-area')).toBeInTheDocument()
// Should still render but handle gracefully
expect(screen.getByTestId('chat-input-area'))!.toBeInTheDocument()
expect(capturedChatInputProps?.inputsForm).toHaveLength(3)
})
})
@ -287,7 +288,7 @@ describe('DebugWithMultipleModel', () => {
rerender(<DebugWithMultipleModel {...props2} />)
const items = screen.getAllByTestId('debug-item')
expect(items[0]).toHaveAttribute('data-model-id', 'model-2')
expect(items[0])!.toHaveAttribute('data-model-id', 'model-2')
})
})
@ -296,14 +297,14 @@ describe('DebugWithMultipleModel', () => {
renderComponent()
const chatInput = screen.getByTestId('chat-input-area')
expect(chatInput).toBeInTheDocument()
expect(chatInput)!.toBeInTheDocument()
// Check for button accessibility
const sendButton = screen.getByRole('button', { name: /send/i })
expect(sendButton).toBeInTheDocument()
expect(sendButton)!.toBeInTheDocument()
const featureButton = screen.getByRole('button', { name: /feature/i })
expect(featureButton).toBeInTheDocument()
expect(featureButton)!.toBeInTheDocument()
})
it('should apply ARIA attributes correctly', () => {
@ -312,8 +313,8 @@ describe('DebugWithMultipleModel', () => {
// Debug items should be identifiable
const debugItem = screen.getByTestId('debug-item')
expect(debugItem).toBeInTheDocument()
expect(debugItem).toHaveAttribute('data-model-id')
expect(debugItem)!.toBeInTheDocument()
expect(debugItem)!.toHaveAttribute('data-model-id')
})
})
@ -422,7 +423,8 @@ describe('DebugWithMultipleModel', () => {
fireEvent.click(screen.getByRole('button', { name: /feature/i }))
// Assert
expect(screen.getByTestId('chat-input-area')).toBeInTheDocument()
// Assert
expect(screen.getByTestId('chat-input-area'))!.toBeInTheDocument()
expect(capturedChatInputProps?.inputs).toEqual({ audience: 'engineers' })
expect(capturedChatInputProps?.inputsForm).toEqual([
expect.objectContaining({ label: 'City', variable: 'city', hide: false, required: true }),
@ -446,7 +448,8 @@ describe('DebugWithMultipleModel', () => {
renderComponent()
// Assert
expect(screen.getByTestId('chat-input-area')).toBeInTheDocument()
// Assert
expect(screen.getByTestId('chat-input-area'))!.toBeInTheDocument()
})
it('should hide chat input when not in chat mode', () => {
@ -459,6 +462,37 @@ describe('DebugWithMultipleModel', () => {
// Act
renderComponent({ multipleModelConfigs })
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
// Assert
expect(screen.queryByTestId('chat-input-area')).not.toBeInTheDocument()
expect(screen.getAllByTestId('debug-item')).toHaveLength(1)
@ -538,7 +572,8 @@ describe('DebugWithMultipleModel', () => {
expect(secondItems).toHaveLength(1)
// Check that the element still renders the same content
expect(firstItems[0]).toHaveTextContent(secondItems[0].textContent || '')
// Check that the element still renders the same content
expect(firstItems[0])!.toHaveTextContent(secondItems[0]!.textContent || '')
})
it('should recalculate size and position when number of models changes', () => {
@ -557,8 +592,8 @@ describe('DebugWithMultipleModel', () => {
)
const twoItems = screen.getAllByTestId('debug-item')
expect(twoItems[0].style.width).toBe('calc(50% - 4px - 24px)')
expect(twoItems[1].style.width).toBe('calc(50% - 4px - 24px)')
expect(twoItems[0]!.style.width).toBe('calc(50% - 4px - 24px)')
expect(twoItems[1]!.style.width).toBe('calc(50% - 4px - 24px)')
})
})
@ -583,7 +618,7 @@ describe('DebugWithMultipleModel', () => {
expect(element.style.height).toBe('')
expect(element.style.transform).toBe(expectation.transform)
expectation.classes?.forEach(cls => expect(element).toHaveClass(cls))
expectation.classes?.forEach(cls => expect(element)!.toHaveClass(cls))
}
it('should arrange items in two-column layout for two models', () => {
@ -596,13 +631,13 @@ describe('DebugWithMultipleModel', () => {
// Assert
expect(items).toHaveLength(2)
expectItemLayout(items[0], {
expectItemLayout(items[0]!, {
width: 'calc(50% - 4px - 24px)',
height: '100%',
transform: 'translateX(0) translateY(0)',
classes: ['mr-2'],
})
expectItemLayout(items[1], {
expectItemLayout(items[1]!, {
width: 'calc(50% - 4px - 24px)',
height: '100%',
transform: 'translateX(calc(100% + 8px)) translateY(0)',
@ -620,19 +655,19 @@ describe('DebugWithMultipleModel', () => {
// Assert
expect(items).toHaveLength(3)
expectItemLayout(items[0], {
expectItemLayout(items[0]!, {
width: 'calc(33.3% - 5.33px - 16px)',
height: '100%',
transform: 'translateX(0) translateY(0)',
classes: ['mr-2'],
})
expectItemLayout(items[1], {
expectItemLayout(items[1]!, {
width: 'calc(33.3% - 5.33px - 16px)',
height: '100%',
transform: 'translateX(calc(100% + 8px)) translateY(0)',
classes: ['mr-2'],
})
expectItemLayout(items[2], {
expectItemLayout(items[2]!, {
width: 'calc(33.3% - 5.33px - 16px)',
height: '100%',
transform: 'translateX(calc(200% + 16px)) translateY(0)',
@ -655,25 +690,25 @@ describe('DebugWithMultipleModel', () => {
// Assert
expect(items).toHaveLength(4)
expectItemLayout(items[0], {
expectItemLayout(items[0]!, {
width: 'calc(50% - 4px - 24px)',
height: 'calc(50% - 4px)',
transform: 'translateX(0) translateY(0)',
classes: ['mr-2', 'mb-2'],
})
expectItemLayout(items[1], {
expectItemLayout(items[1]!, {
width: 'calc(50% - 4px - 24px)',
height: 'calc(50% - 4px)',
transform: 'translateX(calc(100% + 8px)) translateY(0)',
classes: ['mb-2'],
})
expectItemLayout(items[2], {
expectItemLayout(items[2]!, {
width: 'calc(50% - 4px - 24px)',
height: 'calc(50% - 4px)',
transform: 'translateX(0) translateY(calc(100% + 8px))',
classes: ['mr-2'],
})
expectItemLayout(items[3], {
expectItemLayout(items[3]!, {
width: 'calc(50% - 4px - 24px)',
height: 'calc(50% - 4px)',
transform: 'translateX(calc(100% + 8px)) translateY(calc(100% + 8px))',
@ -699,7 +734,7 @@ describe('DebugWithMultipleModel', () => {
it('should set scroll area height for chat modes', () => {
const { container } = renderComponent()
const scrollArea = container.querySelector('.relative.mb-3.grow.overflow-auto.px-6') as HTMLElement
expect(scrollArea).toBeInTheDocument()
expect(scrollArea)!.toBeInTheDocument()
expect(scrollArea.style.height).toBe('calc(100% - 60px)')
})

View File

@ -40,7 +40,7 @@ const ModelParameterTrigger: FC<ModelParameterTriggerProps> = ({
const handleSelectModel = ({ modelId, provider }: { modelId: string, provider: string }) => {
const newModelConfigs = [...multipleModelConfigs]
newModelConfigs[index] = {
...newModelConfigs[index],
...newModelConfigs[index]!,
model: modelId,
provider,
}
@ -49,7 +49,7 @@ const ModelParameterTrigger: FC<ModelParameterTriggerProps> = ({
const handleParamsChange = (params: FormValue) => {
const newModelConfigs = [...multipleModelConfigs]
newModelConfigs[index] = {
...newModelConfigs[index],
...newModelConfigs[index]!,
parameters: params,
}
onMultipleModelConfigsChange(true, newModelConfigs)

View File

@ -591,9 +591,10 @@ describe('DebugWithSingleModel', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} />)
// Verify Chat component is rendered
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
expect(screen.getByTestId('chat-input')).toBeInTheDocument()
expect(screen.getByTestId('send-button')).toBeInTheDocument()
// Verify Chat component is rendered
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
expect(screen.getByTestId('chat-input'))!.toBeInTheDocument()
expect(screen.getByTestId('send-button'))!.toBeInTheDocument()
})
it('should render with custom checkCanSend prop', () => {
@ -601,7 +602,7 @@ describe('DebugWithSingleModel', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} checkCanSend={checkCanSend} />)
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
})
})
@ -620,7 +621,7 @@ describe('DebugWithSingleModel', () => {
expect(mockSsePost).toHaveBeenCalled()
})
expect(mockSsePost.mock.calls[0][0]).toBe('apps/test-app-id/chat-messages')
expect(mockSsePost.mock.calls[0]![0]).toBe('apps/test-app-id/chat-messages')
})
it('should prevent send when checkCanSend returns false', async () => {
@ -666,7 +667,7 @@ describe('DebugWithSingleModel', () => {
expect(mockSsePost).toHaveBeenCalled()
})
const body = mockSsePost.mock.calls[0][1].body
const body = mockSsePost.mock.calls[0]![1].body
expect(body.model_config.opening_statement).toBe('Hello!')
expect(body.model_config.suggested_questions).toEqual(['Q1'])
})
@ -685,7 +686,7 @@ describe('DebugWithSingleModel', () => {
expect(mockSsePost).toHaveBeenCalled()
})
const body = mockSsePost.mock.calls[0][1].body
const body = mockSsePost.mock.calls[0]![1].body
expect(body.model_config.opening_statement).toBe('')
expect(body.model_config.suggested_questions).toEqual([])
})
@ -717,7 +718,7 @@ describe('DebugWithSingleModel', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} />)
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
})
it('should handle missing model in provider list', () => {
@ -735,7 +736,7 @@ describe('DebugWithSingleModel', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} />)
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
})
})
@ -759,7 +760,8 @@ describe('DebugWithSingleModel', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} />)
// Component should render successfully with filtered variables
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
// Component should render successfully with filtered variables
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
})
it('should handle empty prompt variables', () => {
@ -775,7 +777,7 @@ describe('DebugWithSingleModel', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} />)
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
})
})
@ -784,7 +786,7 @@ describe('DebugWithSingleModel', () => {
it('should map tool icons from collection list', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} />)
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
})
it('should handle empty tools list', () => {
@ -802,7 +804,7 @@ describe('DebugWithSingleModel', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} />)
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
})
it('should handle missing collection for tool', () => {
@ -829,7 +831,7 @@ describe('DebugWithSingleModel', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} />)
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
})
})
@ -843,7 +845,7 @@ describe('DebugWithSingleModel', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} />)
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
})
it('should handle missing user profile', () => {
@ -859,7 +861,7 @@ describe('DebugWithSingleModel', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} />)
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
})
it('should handle null completion params', () => {
@ -870,7 +872,7 @@ describe('DebugWithSingleModel', () => {
render(<DebugWithSingleModel ref={ref as RefObject<DebugWithSingleModelRefType>} />)
expect(screen.getByTestId('chat-component')).toBeInTheDocument()
expect(screen.getByTestId('chat-component'))!.toBeInTheDocument()
})
})
@ -940,7 +942,7 @@ describe('DebugWithSingleModel', () => {
expect(mockSsePost).toHaveBeenCalled()
})
const body = mockSsePost.mock.calls[0][1].body
const body = mockSsePost.mock.calls[0]![1].body
expect(body.files).toEqual([])
})
@ -989,7 +991,7 @@ describe('DebugWithSingleModel', () => {
expect(mockSsePost).toHaveBeenCalled()
})
const body = mockSsePost.mock.calls[0][1].body
const body = mockSsePost.mock.calls[0]![1].body
expect(body.files).toHaveLength(1)
})
})

View File

@ -345,7 +345,7 @@ describe('useConfiguration utils', () => {
},
url: '/datasets',
})
expect(state.collectionList[0].icon).toBe('/console/tool.svg')
expect(state.collectionList[0]!.icon).toBe('/console/tool.svg')
expect(state.promptMode).toBe('advanced')
expect(state.nextDataSets).toEqual([{ id: 'dataset-1', name: 'Dataset One' }])
expect(state.annotationConfig).toEqual(expect.objectContaining({

View File

@ -42,7 +42,7 @@ describe('external-data-tool-modal-utils', () => {
it('should build localized providers and default configs for code-based tools', () => {
const providers = buildProviders({
codeBasedExtensionList,
locale: LanguagesSupported[1],
locale: LanguagesSupported[1]!,
t,
})

View File

@ -114,8 +114,8 @@ export const getValidationError = ({
if (!systemTypes.includes(localeData.type as typeof systemTypes[number]) && currentProvider?.form_schema) {
for (let i = 0; i < currentProvider.form_schema.length; i++) {
const form = currentProvider.form_schema[i]
if (!localeData.config?.[form.variable] && form.required) {
return t('errorMessage.valueOfVarRequired', { ns: 'appDebug', key: locale === LanguagesSupported[1] ? form.label['zh-Hans'] : form.label['en-US'] })
if (!localeData.config?.[form!.variable] && form!.required) {
return t('errorMessage.valueOfVarRequired', { ns: 'appDebug', key: locale === LanguagesSupported[1] ? form!.label['zh-Hans'] : form!.label['en-US'] })
}
}
}

View File

@ -40,7 +40,7 @@ export const buildConfigurationFeaturesData = (
},
enabled: !!(modelConfig.file_upload?.enabled || modelConfig.file_upload?.image?.enabled),
allowed_file_types: modelConfig.file_upload?.allowed_file_types || [],
allowed_file_extensions: modelConfig.file_upload?.allowed_file_extensions || [...FILE_EXTS[SupportUploadFileTypes.image], ...FILE_EXTS[SupportUploadFileTypes.video]].map(ext => `.${ext}`),
allowed_file_extensions: modelConfig.file_upload?.allowed_file_extensions || [...(FILE_EXTS[SupportUploadFileTypes.image] ?? []), ...(FILE_EXTS[SupportUploadFileTypes.video] ?? [])].map(ext => `.${ext}`),
allowed_file_upload_methods: modelConfig.file_upload?.allowed_file_upload_methods || modelConfig.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
number_limits: modelConfig.file_upload?.number_limits || modelConfig.file_upload?.image?.number_limits || 3,
fileUploadConfig: fileUploadConfigResponse,

View File

@ -53,6 +53,37 @@ describe('CreateAppTemplateDialog', () => {
it('should not render when show is false', () => {
render(<CreateAppTemplateDialog {...defaultProps} />)
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
// FullScreenModal should not render any content when open is false
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
})
@ -61,14 +92,15 @@ describe('CreateAppTemplateDialog', () => {
render(<CreateAppTemplateDialog {...defaultProps} show={true} />)
// FullScreenModal renders with role="dialog"
expect(screen.getByRole('dialog')).toBeInTheDocument()
expect(screen.getByTestId('app-list')).toBeInTheDocument()
// FullScreenModal renders with role="dialog"
expect(screen.getByRole('dialog'))!.toBeInTheDocument()
expect(screen.getByTestId('app-list'))!.toBeInTheDocument()
})
it('should render create from blank button when onCreateFromBlank is provided', () => {
render(<CreateAppTemplateDialog {...defaultProps} show={true} />)
expect(screen.getByTestId('create-from-blank')).toBeInTheDocument()
expect(screen.getByTestId('create-from-blank'))!.toBeInTheDocument()
})
it('should not render create from blank button when onCreateFromBlank is not provided', () => {
@ -87,7 +119,7 @@ describe('CreateAppTemplateDialog', () => {
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
rerender(<CreateAppTemplateDialog {...defaultProps} show={true} />)
expect(screen.getByRole('dialog')).toBeInTheDocument()
expect(screen.getByRole('dialog'))!.toBeInTheDocument()
})
it('should pass closable prop to FullScreenModal', () => {
@ -97,8 +129,8 @@ describe('CreateAppTemplateDialog', () => {
// Verify that the modal has the proper dialog structure
const dialog = screen.getByRole('dialog')
expect(dialog).toBeInTheDocument()
expect(dialog).toHaveAttribute('aria-modal', 'true')
expect(dialog)!.toBeInTheDocument()
expect(dialog)!.toHaveAttribute('aria-modal', 'true')
})
})
@ -109,11 +141,12 @@ describe('CreateAppTemplateDialog', () => {
// Test that the modal is rendered
const dialog = screen.getByRole('dialog')
expect(dialog).toBeInTheDocument()
expect(dialog)!.toBeInTheDocument()
// Test that AppList component renders (child component interactions)
expect(screen.getByTestId('app-list')).toBeInTheDocument()
expect(screen.getByTestId('app-list-success')).toBeInTheDocument()
// Test that AppList component renders (child component interactions)
expect(screen.getByTestId('app-list'))!.toBeInTheDocument()
expect(screen.getByTestId('app-list-success'))!.toBeInTheDocument()
})
it('should call both onSuccess and onClose when app list success is triggered', () => {
@ -204,8 +237,8 @@ describe('CreateAppTemplateDialog', () => {
// Verify that useKeyPress was called with a function
const calls = mockUseKeyPress.mock.calls
expect(calls.length).toBeGreaterThan(0)
expect(calls[0][0]).toBe('esc')
expect(typeof calls[0][1]).toBe('function')
expect(calls[0]![0]).toBe('esc')
expect(typeof calls[0]![1]).toBe('function')
})
})
@ -243,7 +276,7 @@ describe('CreateAppTemplateDialog', () => {
// Test show state
render(<CreateAppTemplateDialog {...defaultProps} show={true} />)
expect(screen.getByRole('dialog')).toBeInTheDocument()
expect(screen.getByRole('dialog'))!.toBeInTheDocument()
// Test hide state
render(<CreateAppTemplateDialog {...defaultProps} show={false} />)
@ -258,7 +291,7 @@ describe('CreateAppTemplateDialog', () => {
render(<CreateAppTemplateDialog {...propsWithoutOnCreate} show={true} />)
}).not.toThrow()
expect(screen.getByTestId('app-list')).toBeInTheDocument()
expect(screen.getByTestId('app-list'))!.toBeInTheDocument()
expect(screen.queryByTestId('create-from-blank')).not.toBeInTheDocument()
})
@ -273,8 +306,8 @@ describe('CreateAppTemplateDialog', () => {
render(<CreateAppTemplateDialog {...requiredProps} />)
}).not.toThrow()
expect(screen.getByRole('dialog')).toBeInTheDocument()
expect(screen.getByTestId('app-list')).toBeInTheDocument()
expect(screen.getByRole('dialog'))!.toBeInTheDocument()
expect(screen.getByTestId('app-list'))!.toBeInTheDocument()
})
})
})

View File

@ -183,15 +183,15 @@ describe('Apps', () => {
render(<Apps />)
expect(screen.getAllByTestId('app-card')).toHaveLength(6)
expect(screen.getByText('Alpha')).toBeInTheDocument()
expect(screen.getByText('Bravo')).toBeInTheDocument()
expect(screen.getByText('Alpha'))!.toBeInTheDocument()
expect(screen.getByText('Bravo'))!.toBeInTheDocument()
})
it('opens create modal when a template card is clicked', () => {
render(<Apps />)
fireEvent.click(screen.getAllByTestId('app-card')[0])
expect(screen.getByTestId('create-from-template-modal')).toBeInTheDocument()
fireEvent.click(screen.getAllByTestId('app-card')[0]!)
expect(screen.getByTestId('create-from-template-modal'))!.toBeInTheDocument()
})
it('shows no template message when list is empty', () => {
@ -202,8 +202,8 @@ describe('Apps', () => {
render(<Apps />)
expect(screen.getByText('app.newApp.noTemplateFound')).toBeInTheDocument()
expect(screen.getByText('app.newApp.noTemplateFoundTip')).toBeInTheDocument()
expect(screen.getByText('app.newApp.noTemplateFound'))!.toBeInTheDocument()
expect(screen.getByText('app.newApp.noTemplateFoundTip'))!.toBeInTheDocument()
})
it('filters templates by keyword and selected app type', async () => {
@ -214,7 +214,7 @@ describe('Apps', () => {
})
await waitFor(() => {
expect(screen.getByText('Bravo')).toBeInTheDocument()
expect(screen.getByText('Bravo'))!.toBeInTheDocument()
expect(screen.queryByText('Alpha')).not.toBeInTheDocument()
})
@ -224,8 +224,8 @@ describe('Apps', () => {
fireEvent.click(screen.getByTestId('type-selector-chat'))
await waitFor(() => {
expect(screen.getByText('Alpha')).toBeInTheDocument()
expect(screen.getByText('Bravo')).toBeInTheDocument()
expect(screen.getByText('Alpha'))!.toBeInTheDocument()
expect(screen.getByText('Bravo'))!.toBeInTheDocument()
expect(screen.queryByText('Charlie')).not.toBeInTheDocument()
})
})
@ -235,7 +235,7 @@ describe('Apps', () => {
render(<Apps onSuccess={onSuccess} />)
fireEvent.click(screen.getAllByTestId('app-card')[0])
fireEvent.click(screen.getAllByTestId('app-card')[0]!)
fireEvent.click(screen.getByTestId('confirm-create'))
await waitFor(() => {
@ -264,7 +264,7 @@ describe('Apps', () => {
render(<Apps />)
fireEvent.click(screen.getAllByTestId('app-card')[0])
fireEvent.click(screen.getAllByTestId('app-card')[0]!)
fireEvent.click(screen.getByTestId('confirm-create'))
await waitFor(() => {
@ -290,7 +290,7 @@ describe('Apps', () => {
render(<Apps />)
expect(screen.getByRole('status')).toBeInTheDocument()
expect(screen.getByRole('status'))!.toBeInTheDocument()
})
it('should handle an undefined template payload by falling back to the empty state', () => {
@ -301,7 +301,7 @@ describe('Apps', () => {
render(<Apps />)
expect(screen.getByText('app.newApp.noTemplateFound')).toBeInTheDocument()
expect(screen.getByText('app.newApp.noTemplateFound'))!.toBeInTheDocument()
})
it('should filter templates by category and the remaining app modes', async () => {
@ -309,8 +309,8 @@ describe('Apps', () => {
fireEvent.click(screen.getByText('Cat C'))
expect(screen.queryByText('Alpha')).not.toBeInTheDocument()
expect(screen.getByText('Echo')).toBeInTheDocument()
expect(screen.getByText('Foxtrot')).toBeInTheDocument()
expect(screen.getByText('Echo'))!.toBeInTheDocument()
expect(screen.getByText('Foxtrot'))!.toBeInTheDocument()
fireEvent.click(screen.getByTestId('type-selector-advanced'))
await waitFor(() => {
@ -322,13 +322,13 @@ describe('Apps', () => {
fireEvent.click(screen.getByText('Cat C'))
fireEvent.click(screen.getByTestId('type-selector-agent'))
await waitFor(() => {
expect(screen.getByText('Echo')).toBeInTheDocument()
expect(screen.getByText('Echo'))!.toBeInTheDocument()
expect(screen.queryByText('Foxtrot')).not.toBeInTheDocument()
})
fireEvent.click(screen.getByTestId('type-selector-workflow'))
await waitFor(() => {
expect(screen.getByText('Foxtrot')).toBeInTheDocument()
expect(screen.getByText('Foxtrot'))!.toBeInTheDocument()
expect(screen.queryByText('Echo')).not.toBeInTheDocument()
})
@ -356,11 +356,11 @@ describe('Apps', () => {
})
await waitFor(() => {
expect(screen.getByText('Cat A')).toBeInTheDocument()
expect(screen.getByText('Cat A'))!.toBeInTheDocument()
})
fireEvent.click(screen.getAllByTestId('app-card')[0])
expect(screen.getByTestId('create-from-template-modal')).toBeInTheDocument()
fireEvent.click(screen.getAllByTestId('app-card')[0]!)
expect(screen.getByTestId('create-from-template-modal'))!.toBeInTheDocument()
fireEvent.click(screen.getByTestId('hide-create-modal'))

View File

@ -137,10 +137,10 @@ describe('CreateFromDSLModal', () => {
/>,
)
expect(screen.getByText('importFromDSL')).toBeInTheDocument()
expect(screen.getByText('importFromDSL'))!.toBeInTheDocument()
await waitFor(() => {
expect(screen.getByText('demo.yml')).toBeInTheDocument()
expect(screen.getByText('demo.yml'))!.toBeInTheDocument()
})
})
@ -159,7 +159,7 @@ describe('CreateFromDSLModal', () => {
await act(async () => {
fireEvent.click(screen.getByText('importFromDSLUrl'))
})
expect(screen.getByPlaceholderText('importFromDSLUrlPlaceholder')).toBeInTheDocument()
expect(screen.getByPlaceholderText('importFromDSLUrlPlaceholder'))!.toBeInTheDocument()
const closeTrigger = screen.getByText('importFromDSL').parentElement?.querySelector('.cursor-pointer.items-center') as HTMLElement
fireEvent.click(closeTrigger)
@ -222,7 +222,7 @@ describe('CreateFromDSLModal', () => {
)
await waitFor(() => {
expect(screen.getByText('demo.yml')).toBeInTheDocument()
expect(screen.getByText('demo.yml'))!.toBeInTheDocument()
})
await act(async () => {
@ -245,7 +245,7 @@ describe('CreateFromDSLModal', () => {
)
await waitFor(() => {
expect(screen.getByText('demo.yml')).toBeInTheDocument()
expect(screen.getByText('demo.yml'))!.toBeInTheDocument()
})
const removeButton = screen.getByText('demo.yml').closest('.group')?.querySelector('button') as HTMLButtonElement
@ -255,7 +255,7 @@ describe('CreateFromDSLModal', () => {
await waitFor(() => {
expect(screen.queryByText('demo.yml')).not.toBeInTheDocument()
expect(getCreateButton()).toBeDisabled()
expect(getCreateButton())!.toBeDisabled()
})
ahooksMocks.handlers.findLast(item => Array.isArray(item.keys))?.handler()
@ -294,10 +294,10 @@ describe('CreateFromDSLModal', () => {
vi.advanceTimersByTime(300)
})
expect(screen.getAllByText('newApp.appCreateDSLErrorTitle')[0]).toBeInTheDocument()
expect(screen.getAllByText('newApp.appCreateDSLErrorTitle')[0])!.toBeInTheDocument()
await act(async () => {
fireEvent.click(screen.getAllByRole('button', { name: 'newApp.Confirm' })[0])
fireEvent.click(screen.getAllByRole('button', { name: 'newApp.Confirm' })[0]!)
})
expect(mockImportDSLConfirm).toHaveBeenCalledWith({
@ -386,7 +386,7 @@ describe('CreateFromDSLModal', () => {
/>,
)
expect(screen.getByText('apps-full')).toBeInTheDocument()
expect(screen.getByText('apps-full'))!.toBeInTheDocument()
ahooksMocks.handlers.findLast(item => Array.isArray(item.keys))?.handler()
expect(mockImportDSL).toHaveBeenCalledTimes(1)
})
@ -461,12 +461,12 @@ describe('CreateFromDSLModal', () => {
vi.advanceTimersByTime(300)
})
await act(async () => {
fireEvent.click(screen.getAllByRole('button', { name: 'newApp.Confirm' })[0])
fireEvent.click(screen.getAllByRole('button', { name: 'newApp.Confirm' })[0]!)
})
expect(toastMocks.error).toHaveBeenCalledWith('newApp.appCreateFailed')
await act(async () => {
fireEvent.click(screen.getAllByRole('button', { name: 'newApp.Confirm' })[0])
fireEvent.click(screen.getAllByRole('button', { name: 'newApp.Confirm' })[0]!)
})
expect(toastMocks.error).toHaveBeenCalledTimes(2)
})

View File

@ -77,7 +77,7 @@ describe('Filter', () => {
it('should render filter components', () => {
render(<Filter {...defaultProps} />)
expect(screen.getByPlaceholderText('operation.search')).toBeInTheDocument()
expect(screen.getByPlaceholderText('operation.search'))!.toBeInTheDocument()
})
it('should return null when loading', () => {
@ -89,13 +89,13 @@ describe('Filter', () => {
it('should render sort component in chat mode', () => {
render(<Filter {...defaultProps} isChatMode />)
expect(screen.getByPlaceholderText('operation.search')).toBeInTheDocument()
expect(screen.getByPlaceholderText('operation.search'))!.toBeInTheDocument()
})
it('should not render sort component when not in chat mode', () => {
render(<Filter {...defaultProps} isChatMode={false} />)
expect(screen.getByPlaceholderText('operation.search')).toBeInTheDocument()
expect(screen.getByPlaceholderText('operation.search'))!.toBeInTheDocument()
})
})
@ -105,23 +105,23 @@ describe('Filter', () => {
})
it('should have today period with value 0', () => {
expect(TIME_PERIOD_MAPPING['1'].value).toBe(0)
expect(TIME_PERIOD_MAPPING['1'].name).toBe('today')
expect(TIME_PERIOD_MAPPING['1']!.value).toBe(0)
expect(TIME_PERIOD_MAPPING['1']!.name).toBe('today')
})
it('should have last7days period with value 7', () => {
expect(TIME_PERIOD_MAPPING['2'].value).toBe(7)
expect(TIME_PERIOD_MAPPING['2'].name).toBe('last7days')
expect(TIME_PERIOD_MAPPING['2']!.value).toBe(7)
expect(TIME_PERIOD_MAPPING['2']!.name).toBe('last7days')
})
it('should have last4weeks period with value 28', () => {
expect(TIME_PERIOD_MAPPING['3'].value).toBe(28)
expect(TIME_PERIOD_MAPPING['3'].name).toBe('last4weeks')
expect(TIME_PERIOD_MAPPING['3']!.value).toBe(28)
expect(TIME_PERIOD_MAPPING['3']!.name).toBe('last4weeks')
})
it('should have allTime period with value -1', () => {
expect(TIME_PERIOD_MAPPING['9'].value).toBe(-1)
expect(TIME_PERIOD_MAPPING['9'].name).toBe('allTime')
expect(TIME_PERIOD_MAPPING['9']!.value).toBe(-1)
expect(TIME_PERIOD_MAPPING['9']!.name).toBe('allTime')
})
})
@ -158,25 +158,25 @@ describe('Filter', () => {
it('should update and clear period, annotation, and sort filters', () => {
render(<Filter {...defaultProps} isChatMode />)
fireEvent.click(screen.getAllByText('select-9')[0])
fireEvent.click(screen.getAllByText('select-9')[0]!)
expect(mockSetQueryParams).toHaveBeenCalledWith({
...defaultQueryParams,
period: '9',
})
fireEvent.click(screen.getAllByText('clear-chip')[0])
fireEvent.click(screen.getAllByText('clear-chip')[0]!)
expect(mockSetQueryParams).toHaveBeenCalledWith({
...defaultQueryParams,
period: '9',
})
fireEvent.click(screen.getAllByText('select-not_annotated')[0])
fireEvent.click(screen.getAllByText('select-not_annotated')[0]!)
expect(mockSetQueryParams).toHaveBeenCalledWith({
...defaultQueryParams,
annotation_status: 'not_annotated',
})
fireEvent.click(screen.getAllByText('clear-chip')[1])
fireEvent.click(screen.getAllByText('clear-chip')[1]!)
expect(mockSetQueryParams).toHaveBeenCalledWith({
...defaultQueryParams,
annotation_status: 'all',
@ -200,7 +200,8 @@ describe('Filter', () => {
render(<Filter {...propsWithPeriod} />)
// Period '1' maps to 'today' in TIME_PERIOD_MAPPING
expect(screen.getByText('filter.period.today')).toBeInTheDocument()
// Period '1' maps to 'today' in TIME_PERIOD_MAPPING
expect(screen.getByText('filter.period.today'))!.toBeInTheDocument()
})
it('should display "last7days" when period is set to 2', () => {
@ -211,14 +212,15 @@ describe('Filter', () => {
render(<Filter {...propsWithPeriod} />)
expect(screen.getByText('filter.period.last7days')).toBeInTheDocument()
expect(screen.getByText('filter.period.last7days'))!.toBeInTheDocument()
})
it('should display "allTime" when period is set to 9', () => {
render(<Filter {...defaultProps} />)
// Default period is '9' which maps to 'allTime'
expect(screen.getByText('filter.period.allTime')).toBeInTheDocument()
// Default period is '9' which maps to 'allTime'
expect(screen.getByText('filter.period.allTime'))!.toBeInTheDocument()
})
it('should display annotated status with count when annotation_status is annotated', () => {
@ -230,7 +232,8 @@ describe('Filter', () => {
render(<Filter {...propsWithAnnotation} />)
// The mock returns count: 10, so the text should include the count
expect(screen.getByText('filter.annotation.annotated (10)')).toBeInTheDocument()
// The mock returns count: 10, so the text should include the count
expect(screen.getByText('filter.annotation.annotated (10)'))!.toBeInTheDocument()
})
it('should display not_annotated status when annotation_status is not_annotated', () => {
@ -241,14 +244,15 @@ describe('Filter', () => {
render(<Filter {...propsWithNotAnnotated} />)
expect(screen.getByText('filter.annotation.not_annotated')).toBeInTheDocument()
expect(screen.getByText('filter.annotation.not_annotated'))!.toBeInTheDocument()
})
it('should display all annotation status when annotation_status is all', () => {
render(<Filter {...defaultProps} />)
// Default annotation_status is 'all'
expect(screen.getByText('filter.annotation.all')).toBeInTheDocument()
// Default annotation_status is 'all'
expect(screen.getByText('filter.annotation.all'))!.toBeInTheDocument()
})
it('should return null when annotation count data is unavailable', () => {
@ -269,7 +273,7 @@ describe('Filter', () => {
render(<Filter {...propsWithSort} />)
expect(screen.getByPlaceholderText('operation.search')).toBeInTheDocument()
expect(screen.getByPlaceholderText('operation.search'))!.toBeInTheDocument()
})
it('should handle descending sort order', () => {
@ -281,7 +285,7 @@ describe('Filter', () => {
render(<Filter {...propsWithDescSort} />)
expect(screen.getByPlaceholderText('operation.search')).toBeInTheDocument()
expect(screen.getByPlaceholderText('operation.search'))!.toBeInTheDocument()
})
})
})

View File

@ -150,12 +150,12 @@ describe('log list utils', () => {
it('should update annotation state helpers', () => {
const items = createChatItems()
expect(applyAnnotationEdited(items, 'updated question', 'updated answer', 1)[0].content).toBe('updated question')
expect(applyAnnotationAdded(items, 'annotation-1', 'Dify', 'question', 'answer', 1)[1].annotation).toEqual(expect.objectContaining({
expect(applyAnnotationEdited(items, 'updated question', 'updated answer', 1)[0]!.content).toBe('updated question')
expect(applyAnnotationAdded(items, 'annotation-1', 'Dify', 'question', 'answer', 1)[1]!.annotation).toEqual(expect.objectContaining({
id: 'annotation-1',
authorName: 'Dify',
}))
expect(applyAnnotationRemoved(items, 1)[1].annotation).toBeUndefined()
expect(applyAnnotationRemoved(items, 1)[1]!.annotation).toBeUndefined()
})
it('should derive urls, scroll thresholds, row values, and detail metadata', () => {

View File

@ -78,7 +78,7 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
limit,
...((debouncedQueryParams.period !== '9')
? {
start: dayjs().subtract(TIME_PERIOD_MAPPING[debouncedQueryParams.period].value, 'day').startOf('day').format('YYYY-MM-DD HH:mm'),
start: dayjs().subtract(TIME_PERIOD_MAPPING[debouncedQueryParams.period]!.value, 'day').startOf('day').format('YYYY-MM-DD HH:mm'),
end: dayjs().endOf('day').format('YYYY-MM-DD HH:mm'),
}
: {}),

View File

@ -97,7 +97,7 @@ describe('app-chart-utils', () => {
expect(dataset.dimensions).toEqual(['date', 'count'])
expect(dataset.source).toHaveLength(2)
expect(yAxis.max).toBe(100)
expect(series[0].lineStyle.color).toBe('rgba(6, 148, 162, 1)')
expect(series[0]!.lineStyle.color).toBe('rgba(6, 148, 162, 1)')
})
it('should build token-aware tooltip content and split-line intervals for cost charts', () => {
@ -112,11 +112,11 @@ describe('app-chart-utils', () => {
})
const xAxis = options.xAxis as Array<Record<string, any>>
const formatter = xAxis[0].axisLabel.formatter as (value: string) => string
const outerInterval = xAxis[0].splitLine.interval as (index: number) => boolean
const innerInterval = xAxis[1].splitLine.interval as (_index: number, value: string) => boolean
const formatter = xAxis[0]!.axisLabel.formatter as (value: string) => string
const outerInterval = xAxis[0]!.splitLine.interval as (index: number) => boolean
const innerInterval = xAxis[1]!.splitLine.interval as (_index: number, value: string) => boolean
const series = options.series as Array<Record<string, any>>
const tooltipFormatter = series[0].tooltip.formatter as (params: { name: string, data: { total_cost: number, total_price: string } }) => string
const tooltipFormatter = series[0]!.tooltip.formatter as (params: { name: string, data: { total_cost: number, total_price: string } }) => string
expect(formatter('Jan 2, 2024')).toBe('Jan 2, 2024')
expect(outerInterval(0)).toBe(true)

View File

@ -58,10 +58,10 @@ describe('app-chart', () => {
/>,
)
expect(screen.getByText('Cost title')).toBeInTheDocument()
expect(screen.getByText('300')).toBeInTheDocument()
expect(screen.getByText(/\$3\.7500/)).toBeInTheDocument()
expect(screen.getByTestId('echarts')).toBeInTheDocument()
expect(screen.getByText('Cost title'))!.toBeInTheDocument()
expect(screen.getByText('300'))!.toBeInTheDocument()
expect(screen.getByText(/\$3\.7500/))!.toBeInTheDocument()
expect(screen.getByTestId('echarts'))!.toBeInTheDocument()
})
})
@ -85,10 +85,10 @@ describe('app-chart', () => {
/>,
)
expect(screen.getByText('analysis.totalMessages.title')).toBeInTheDocument()
expect(screen.getByTestId('echarts')).toBeInTheDocument()
expect(screen.getByText('analysis.totalMessages.title'))!.toBeInTheDocument()
expect(screen.getByTestId('echarts'))!.toBeInTheDocument()
const options = reactEChartsMock.mock.calls[0][0] as {
const options = reactEChartsMock.mock.calls[0]![0] as {
dataset: { source: Array<Record<string, unknown>> }
yAxis: { max: number }
}

View File

@ -102,12 +102,12 @@ describe('Embedded', () => {
const optionButtons = document.body.querySelectorAll('[class*="option"]')
expect(optionButtons.length).toBeGreaterThanOrEqual(3)
act(() => {
fireEvent.click(optionButtons[2])
fireEvent.click(optionButtons[2]!)
})
const [chromeText] = screen.getAllByText('appOverview.overview.appInfo.embedded.chromePlugin')
act(() => {
fireEvent.click(chromeText)
fireEvent.click(chromeText!)
})
expect(mockWindowOpen).toHaveBeenCalledWith(

View File

@ -105,7 +105,7 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, classNam
if (option === 'chromePlugin') {
const splitUrl = OPTION_MAP[option].getContent(appBaseUrl, accessToken).split(': ')
if (splitUrl.length > 1)
copy(splitUrl[1])
copy(splitUrl[1]!)
}
else {
copy(OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#1C64F2', isTestEnv))

View File

@ -37,8 +37,8 @@ describe('SavedItems', () => {
const { container } = render(<SavedItems {...baseProps} />)
const markdownElement = container.querySelector('.markdown-body')
expect(markdownElement).toBeInTheDocument()
expect(screen.getByText('11 common.unit.char')).toBeInTheDocument()
expect(markdownElement)!.toBeInTheDocument()
expect(screen.getByText('11 common.unit.char'))!.toBeInTheDocument()
const actionArea = container.querySelector('[class*="bg-components-actionbar-bg"]')
const actionButtons = actionArea?.querySelectorAll('button') ?? []
@ -56,11 +56,11 @@ describe('SavedItems', () => {
const copyButton = actionButtons[1]
const deleteButton = actionButtons[2]
fireEvent.click(copyButton)
fireEvent.click(copyButton!)
expect(mockCopy).toHaveBeenCalledWith('hello world')
expect(toastSuccessSpy).toHaveBeenCalledWith('common.actionMsg.copySuccessfully')
fireEvent.click(deleteButton)
fireEvent.click(deleteButton!)
expect(handleRemove).toHaveBeenCalledWith('1')
})
})

View File

@ -148,9 +148,9 @@ function AppTypeSelectTrigger({ values }: { readonly values: AppSelectorProps['v
'flex h-8 flex-nowrap items-center justify-between gap-1',
)}
>
<AppTypeIcon type={values[0]} />
<AppTypeIcon type={values[0]!} />
<div className="line-clamp-1 flex flex-1 items-center text-center">
<AppTypeLabel type={values[0]} className="system-sm-medium text-components-menu-item-text" />
<AppTypeLabel type={values[0]!} className="system-sm-medium text-components-menu-item-text" />
</div>
</div>
)

View File

@ -56,8 +56,9 @@ describe('Filter', () => {
)
// Should render status chip, period chip, and search input
expect(screen.getByText('All')).toBeInTheDocument()
expect(screen.getByPlaceholderText('common.operation.search')).toBeInTheDocument()
// Should render status chip, period chip, and search input
expect(screen.getByText('All'))!.toBeInTheDocument()
expect(screen.getByPlaceholderText('common.operation.search'))!.toBeInTheDocument()
})
it('should render all filter components', () => {
@ -69,11 +70,14 @@ describe('Filter', () => {
)
// Status chip
expect(screen.getByText('All')).toBeInTheDocument()
// Status chip
expect(screen.getByText('All'))!.toBeInTheDocument()
// Period chip (shows translated key)
expect(screen.getByText('appLog.filter.period.last7days')).toBeInTheDocument()
// Period chip (shows translated key)
expect(screen.getByText('appLog.filter.period.last7days'))!.toBeInTheDocument()
// Search input
expect(screen.getByPlaceholderText('common.operation.search')).toBeInTheDocument()
// Search input
expect(screen.getByPlaceholderText('common.operation.search'))!.toBeInTheDocument()
})
})
@ -90,7 +94,8 @@ describe('Filter', () => {
)
// Chip should show Success for succeeded status
expect(screen.getByText('Success')).toBeInTheDocument()
// Chip should show Success for succeeded status
expect(screen.getByText('Success'))!.toBeInTheDocument()
})
it('should open status dropdown when clicked', async () => {
@ -107,10 +112,10 @@ describe('Filter', () => {
// Should show all status options
await waitFor(() => {
expect(screen.getByText('Success')).toBeInTheDocument()
expect(screen.getByText('Fail')).toBeInTheDocument()
expect(screen.getByText('Stop')).toBeInTheDocument()
expect(screen.getByText('Partial Success')).toBeInTheDocument()
expect(screen.getByText('Success'))!.toBeInTheDocument()
expect(screen.getByText('Fail'))!.toBeInTheDocument()
expect(screen.getByText('Stop'))!.toBeInTheDocument()
expect(screen.getByText('Partial Success'))!.toBeInTheDocument()
})
})
@ -167,7 +172,7 @@ describe('Filter', () => {
// Find the clear icon (div with group/clear class) in the status chip
const clearIcon = container.querySelector('.group\\/clear')
expect(clearIcon).toBeInTheDocument()
expect(clearIcon)!.toBeInTheDocument()
await user.click(clearIcon!)
expect(setQueryParams).toHaveBeenCalledWith({
@ -190,7 +195,7 @@ describe('Filter', () => {
/>,
)
expect(screen.getByText(expectedLabel)).toBeInTheDocument()
expect(screen.getByText(expectedLabel))!.toBeInTheDocument()
})
})
@ -206,7 +211,7 @@ describe('Filter', () => {
/>,
)
expect(screen.getByText('appLog.filter.period.today')).toBeInTheDocument()
expect(screen.getByText('appLog.filter.period.today'))!.toBeInTheDocument()
})
it('should open period dropdown when clicked', async () => {
@ -223,10 +228,10 @@ describe('Filter', () => {
// Should show all period options
await waitFor(() => {
expect(screen.getByText('appLog.filter.period.today')).toBeInTheDocument()
expect(screen.getByText('appLog.filter.period.last4weeks')).toBeInTheDocument()
expect(screen.getByText('appLog.filter.period.last3months')).toBeInTheDocument()
expect(screen.getByText('appLog.filter.period.allTime')).toBeInTheDocument()
expect(screen.getByText('appLog.filter.period.today'))!.toBeInTheDocument()
expect(screen.getByText('appLog.filter.period.last4weeks'))!.toBeInTheDocument()
expect(screen.getByText('appLog.filter.period.last3months'))!.toBeInTheDocument()
expect(screen.getByText('appLog.filter.period.allTime'))!.toBeInTheDocument()
})
})
@ -287,7 +292,7 @@ describe('Filter', () => {
/>,
)
expect(screen.getByDisplayValue('test search')).toBeInTheDocument()
expect(screen.getByDisplayValue('test search'))!.toBeInTheDocument()
})
it('should call setQueryParams when typing in search', async () => {
@ -337,7 +342,7 @@ describe('Filter', () => {
// Find the clear icon div (has cursor-pointer class and contains RiCloseCircleFill)
const clearIconDiv = inputWrapper?.querySelector('div.cursor-pointer')
expect(clearIconDiv).toBeInTheDocument()
expect(clearIconDiv)!.toBeInTheDocument()
await user.click(clearIconDiv!)
expect(setQueryParams).toHaveBeenCalledWith({
@ -399,11 +404,11 @@ describe('Filter', () => {
['9', 'allTime', -1],
])('TIME_PERIOD_MAPPING[%s] should have name=%s and correct value', (key, name, expectedValue) => {
const mapping = TIME_PERIOD_MAPPING[key]
expect(mapping.name).toBe(name)
expect(mapping!.name).toBe(name)
if (expectedValue >= 0)
expect(mapping.value).toBe(expectedValue)
expect(mapping!.value).toBe(expectedValue)
else
expect(mapping.value).toBe(-1)
expect(mapping!.value).toBe(-1)
})
})
@ -420,7 +425,7 @@ describe('Filter', () => {
)
const input = screen.getByPlaceholderText('common.operation.search')
expect(input).toHaveValue('')
expect(input)!.toHaveValue('')
})
it('should handle empty string keyword', () => {
@ -432,7 +437,7 @@ describe('Filter', () => {
)
const input = screen.getByPlaceholderText('common.operation.search')
expect(input).toHaveValue('')
expect(input)!.toHaveValue('')
})
it('should preserve other query params when updating status', async () => {
@ -515,9 +520,9 @@ describe('Filter', () => {
/>,
)
expect(screen.getByText('Success')).toBeInTheDocument()
expect(screen.getByText('appLog.filter.period.today')).toBeInTheDocument()
expect(screen.getByDisplayValue('integration test')).toBeInTheDocument()
expect(screen.getByText('Success'))!.toBeInTheDocument()
expect(screen.getByText('appLog.filter.period.today'))!.toBeInTheDocument()
expect(screen.getByDisplayValue('integration test'))!.toBeInTheDocument()
})
it('should have proper layout with flex and gap', () => {
@ -529,9 +534,9 @@ describe('Filter', () => {
)
const filterContainer = container.firstChild as HTMLElement
expect(filterContainer).toHaveClass('flex')
expect(filterContainer).toHaveClass('flex-row')
expect(filterContainer).toHaveClass('gap-2')
expect(filterContainer)!.toHaveClass('flex')
expect(filterContainer)!.toHaveClass('flex-row')
expect(filterContainer)!.toHaveClass('gap-2')
})
})
})

View File

@ -181,7 +181,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={undefined} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(container.querySelector('.spin-animation')).toBeInTheDocument()
expect(container.querySelector('.spin-animation'))!.toBeInTheDocument()
})
it('should render loading state when appDetail is undefined', () => {
@ -191,7 +191,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={undefined} onRefresh={defaultOnRefresh} />,
)
expect(container.querySelector('.spin-animation')).toBeInTheDocument()
expect(container.querySelector('.spin-animation'))!.toBeInTheDocument()
})
it('should render table when data is available', () => {
@ -201,7 +201,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByRole('table')).toBeInTheDocument()
expect(screen.getByRole('table'))!.toBeInTheDocument()
})
it('should render all table headers', () => {
@ -211,11 +211,11 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('appLog.table.header.startTime')).toBeInTheDocument()
expect(screen.getByText('appLog.table.header.status')).toBeInTheDocument()
expect(screen.getByText('appLog.table.header.runtime')).toBeInTheDocument()
expect(screen.getByText('appLog.table.header.tokens')).toBeInTheDocument()
expect(screen.getByText('appLog.table.header.user')).toBeInTheDocument()
expect(screen.getByText('appLog.table.header.startTime'))!.toBeInTheDocument()
expect(screen.getByText('appLog.table.header.status'))!.toBeInTheDocument()
expect(screen.getByText('appLog.table.header.runtime'))!.toBeInTheDocument()
expect(screen.getByText('appLog.table.header.tokens'))!.toBeInTheDocument()
expect(screen.getByText('appLog.table.header.user'))!.toBeInTheDocument()
})
it('should render trigger column for workflow apps', () => {
@ -226,7 +226,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={workflowApp} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('appLog.table.header.triggered_from')).toBeInTheDocument()
expect(screen.getByText('appLog.table.header.triggered_from'))!.toBeInTheDocument()
})
it('should not render trigger column for non-workflow apps', () => {
@ -256,7 +256,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('Success')).toBeInTheDocument()
expect(screen.getByText('Success'))!.toBeInTheDocument()
})
it('should render failure status correctly', () => {
@ -270,7 +270,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('Failure')).toBeInTheDocument()
expect(screen.getByText('Failure'))!.toBeInTheDocument()
})
it('should render stopped status correctly', () => {
@ -284,7 +284,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('Stop')).toBeInTheDocument()
expect(screen.getByText('Stop'))!.toBeInTheDocument()
})
it('should render running status correctly', () => {
@ -298,7 +298,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('Running')).toBeInTheDocument()
expect(screen.getByText('Running'))!.toBeInTheDocument()
})
it('should render partial-succeeded status correctly', () => {
@ -312,7 +312,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('Partial Success')).toBeInTheDocument()
expect(screen.getByText('Partial Success'))!.toBeInTheDocument()
})
})
@ -332,7 +332,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('John Doe')).toBeInTheDocument()
expect(screen.getByText('John Doe'))!.toBeInTheDocument()
})
it('should display end user session id when created by end user', () => {
@ -347,7 +347,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('session-abc-123')).toBeInTheDocument()
expect(screen.getByText('session-abc-123'))!.toBeInTheDocument()
})
it('should display N/A when no user info', () => {
@ -362,7 +362,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('N/A')).toBeInTheDocument()
expect(screen.getByText('N/A'))!.toBeInTheDocument()
})
})
@ -405,7 +405,7 @@ describe('WorkflowAppLogList', () => {
// Arrow should rotate (indicated by class change)
// The sort icon should have rotate-180 class for ascending
const sortIcon = startTimeHeader.closest('div')?.querySelector('svg')
expect(sortIcon).toBeInTheDocument()
expect(sortIcon)!.toBeInTheDocument()
})
it('should render sort arrow icon', () => {
@ -417,7 +417,7 @@ describe('WorkflowAppLogList', () => {
// Check for ArrowDownIcon presence
const sortArrow = container.querySelector('svg.ml-0\\.5')
expect(sortArrow).toBeInTheDocument()
expect(sortArrow)!.toBeInTheDocument()
})
})
@ -440,11 +440,11 @@ describe('WorkflowAppLogList', () => {
)
const dataRows = screen.getAllByRole('row')
await user.click(dataRows[1]) // Click first data row
await user.click(dataRows[1]!) // Click first data row
const dialog = await screen.findByRole('dialog')
expect(dialog).toBeInTheDocument()
expect(screen.getByText('appLog.runDetail.workflowTitle')).toBeInTheDocument()
expect(dialog)!.toBeInTheDocument()
expect(screen.getByText('appLog.runDetail.workflowTitle'))!.toBeInTheDocument()
})
it('should close drawer and call onRefresh when closing', async () => {
@ -459,7 +459,7 @@ describe('WorkflowAppLogList', () => {
// Open drawer
const dataRows = screen.getAllByRole('row')
await user.click(dataRows[1])
await user.click(dataRows[1]!)
await screen.findByRole('dialog')
// Close drawer using Escape key
@ -482,14 +482,46 @@ describe('WorkflowAppLogList', () => {
const dataRows = screen.getAllByRole('row')
const dataRow = dataRows[1]
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
// Before click - no highlight
expect(dataRow).not.toHaveClass('bg-background-default-hover')
// After click - has highlight (via currentLog state)
await user.click(dataRow)
await user.click(dataRow!)
// The row should have the selected class
expect(dataRow).toHaveClass('bg-background-default-hover')
// The row should have the selected class
expect(dataRow)!.toHaveClass('bg-background-default-hover')
})
})
@ -515,7 +547,7 @@ describe('WorkflowAppLogList', () => {
// Open drawer
const dataRows = screen.getAllByRole('row')
await user.click(dataRows[1])
await user.click(dataRows[1]!)
await screen.findByRole('dialog')
// Replay button should be present for app-run triggers
@ -543,12 +575,12 @@ describe('WorkflowAppLogList', () => {
// Open drawer
const dataRows = screen.getAllByRole('row')
await user.click(dataRows[1])
await user.click(dataRows[1]!)
await screen.findByRole('dialog')
// Replay button should be present for debugging triggers
const replayButton = screen.getByRole('button', { name: 'appLog.runDetail.testWithParams' })
expect(replayButton).toBeInTheDocument()
expect(replayButton)!.toBeInTheDocument()
})
it('should not show replay for webhook triggers', async () => {
@ -569,9 +601,40 @@ describe('WorkflowAppLogList', () => {
// Open drawer
const dataRows = screen.getAllByRole('row')
await user.click(dataRows[1])
await user.click(dataRows[1]!)
await screen.findByRole('dialog')
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
// Replay button should not be present for webhook triggers
expect(screen.queryByRole('button', { name: 'appLog.runDetail.testWithParams' })).not.toBeInTheDocument()
})
@ -594,7 +657,7 @@ describe('WorkflowAppLogList', () => {
// Unread indicator is a small blue dot
const unreadDot = container.querySelector('.bg-util-colors-blue-blue-500')
expect(unreadDot).toBeInTheDocument()
expect(unreadDot)!.toBeInTheDocument()
})
it('should not show unread indicator for read logs', () => {
@ -629,7 +692,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('1.235s')).toBeInTheDocument()
expect(screen.getByText('1.235s'))!.toBeInTheDocument()
})
it('should display 0 elapsed time with special styling', () => {
@ -644,8 +707,8 @@ describe('WorkflowAppLogList', () => {
)
const zeroTime = screen.getByText('0.000s')
expect(zeroTime).toBeInTheDocument()
expect(zeroTime).toHaveClass('text-text-quaternary')
expect(zeroTime)!.toBeInTheDocument()
expect(zeroTime)!.toHaveClass('text-text-quaternary')
})
})
@ -664,7 +727,7 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('12345')).toBeInTheDocument()
expect(screen.getByText('12345'))!.toBeInTheDocument()
})
})
@ -680,7 +743,7 @@ describe('WorkflowAppLogList', () => {
)
const table = screen.getByRole('table')
expect(table).toBeInTheDocument()
expect(table)!.toBeInTheDocument()
// Should only have header row
const rows = screen.getAllByRole('row')
@ -721,8 +784,8 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={createMockApp()} onRefresh={defaultOnRefresh} />,
)
expect(screen.getByText('0.000s')).toBeInTheDocument()
expect(screen.getByText('0')).toBeInTheDocument()
expect(screen.getByText('0.000s'))!.toBeInTheDocument()
expect(screen.getByText('0'))!.toBeInTheDocument()
})
it('should handle null workflow_run.triggered_from for non-workflow apps', () => {
@ -739,6 +802,37 @@ describe('WorkflowAppLogList', () => {
<WorkflowAppLogList logs={logs} appDetail={chatApp} onRefresh={defaultOnRefresh} />,
)
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
// Should render without trigger column
expect(screen.queryByText('appLog.table.header.triggered_from')).not.toBeInTheDocument()
})

View File

@ -47,7 +47,7 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
...(debouncedQueryParams.keyword ? { keyword: debouncedQueryParams.keyword } : {}),
...((debouncedQueryParams.period !== '9')
? {
created_at__after: dayjs().subtract(TIME_PERIOD_MAPPING[debouncedQueryParams.period].value, 'day').startOf('day').tz(timezone).format('YYYY-MM-DDTHH:mm:ssZ'),
created_at__after: dayjs().subtract(TIME_PERIOD_MAPPING[debouncedQueryParams.period]!.value, 'day').startOf('day').tz(timezone).format('YYYY-MM-DDTHH:mm:ssZ'),
created_at__before: dayjs().endOf('day').tz(timezone).format('YYYY-MM-DDTHH:mm:ssZ'),
}
: {}),

View File

@ -222,55 +222,55 @@ describe('List', () => {
describe('Rendering', () => {
it('should render without crashing', () => {
renderList()
expect(screen.getByText('app.types.all')).toBeInTheDocument()
expect(screen.getByText('app.types.all'))!.toBeInTheDocument()
})
it('should render tab slider with all app types', () => {
renderList()
expect(screen.getByText('app.types.all')).toBeInTheDocument()
expect(screen.getByText('app.types.workflow')).toBeInTheDocument()
expect(screen.getByText('app.types.advanced')).toBeInTheDocument()
expect(screen.getByText('app.types.chatbot')).toBeInTheDocument()
expect(screen.getByText('app.types.agent')).toBeInTheDocument()
expect(screen.getByText('app.types.completion')).toBeInTheDocument()
expect(screen.getByText('app.types.all'))!.toBeInTheDocument()
expect(screen.getByText('app.types.workflow'))!.toBeInTheDocument()
expect(screen.getByText('app.types.advanced'))!.toBeInTheDocument()
expect(screen.getByText('app.types.chatbot'))!.toBeInTheDocument()
expect(screen.getByText('app.types.agent'))!.toBeInTheDocument()
expect(screen.getByText('app.types.completion'))!.toBeInTheDocument()
})
it('should render search input', () => {
renderList()
expect(screen.getByRole('textbox')).toBeInTheDocument()
expect(screen.getByRole('textbox'))!.toBeInTheDocument()
})
it('should render tag filter', () => {
renderList()
expect(screen.getByText('common.tag.placeholder')).toBeInTheDocument()
expect(screen.getByText('common.tag.placeholder'))!.toBeInTheDocument()
})
it('should render created by me checkbox', () => {
renderList()
expect(screen.getByText('app.showMyCreatedAppsOnly')).toBeInTheDocument()
expect(screen.getByText('app.showMyCreatedAppsOnly'))!.toBeInTheDocument()
})
it('should render app cards when apps exist', () => {
renderList()
expect(screen.getByTestId('app-card-app-1')).toBeInTheDocument()
expect(screen.getByTestId('app-card-app-2')).toBeInTheDocument()
expect(screen.getByTestId('app-card-app-1'))!.toBeInTheDocument()
expect(screen.getByTestId('app-card-app-2'))!.toBeInTheDocument()
})
it('should render new app card for editors', () => {
renderList()
expect(screen.getByTestId('new-app-card')).toBeInTheDocument()
expect(screen.getByTestId('new-app-card'))!.toBeInTheDocument()
})
it('should render footer when branding is disabled', () => {
renderList()
expect(screen.getByTestId('footer')).toBeInTheDocument()
expect(screen.getByTestId('footer'))!.toBeInTheDocument()
})
it('should render drop DSL hint for editors', () => {
renderList()
expect(screen.getByText('app.newApp.dropDSLToCreateApp')).toBeInTheDocument()
expect(screen.getByText('app.newApp.dropDSLToCreateApp'))!.toBeInTheDocument()
})
})
@ -281,7 +281,7 @@ describe('List', () => {
fireEvent.click(screen.getByText('app.types.workflow'))
await vi.waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
const lastCall = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const lastCall = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(lastCall.searchParams.get('category')).toBe(AppModeEnum.WORKFLOW)
})
@ -291,7 +291,7 @@ describe('List', () => {
fireEvent.click(screen.getByText('app.types.all'))
await vi.waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
const lastCall = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const lastCall = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
// nuqs removes the default value ('all') from URL params
expect(lastCall.searchParams.has('category')).toBe(false)
})
@ -300,7 +300,7 @@ describe('List', () => {
describe('Search Functionality', () => {
it('should render search input field', () => {
renderList()
expect(screen.getByRole('textbox')).toBeInTheDocument()
expect(screen.getByRole('textbox'))!.toBeInTheDocument()
})
it('should handle search input change', () => {
@ -318,7 +318,7 @@ describe('List', () => {
renderList()
const clearButton = document.querySelector('.group')
expect(clearButton).toBeInTheDocument()
expect(clearButton)!.toBeInTheDocument()
if (clearButton)
fireEvent.click(clearButton)
@ -329,14 +329,14 @@ describe('List', () => {
describe('Tag Filter', () => {
it('should render tag filter component', () => {
renderList()
expect(screen.getByText('common.tag.placeholder')).toBeInTheDocument()
expect(screen.getByText('common.tag.placeholder'))!.toBeInTheDocument()
})
})
describe('Created By Me Filter', () => {
it('should render checkbox with correct label', () => {
renderList()
expect(screen.getByText('app.showMyCreatedAppsOnly')).toBeInTheDocument()
expect(screen.getByText('app.showMyCreatedAppsOnly'))!.toBeInTheDocument()
})
it('should handle checkbox change', () => {
@ -391,39 +391,39 @@ describe('List', () => {
describe('Edge Cases', () => {
it('should handle multiple renders without issues', () => {
const { unmount } = renderWithNuqs(<List />)
expect(screen.getByText('app.types.all')).toBeInTheDocument()
expect(screen.getByText('app.types.all'))!.toBeInTheDocument()
unmount()
renderList()
expect(screen.getByText('app.types.all')).toBeInTheDocument()
expect(screen.getByText('app.types.all'))!.toBeInTheDocument()
})
it('should render app cards correctly', () => {
renderList()
expect(screen.getByText('Test App 1')).toBeInTheDocument()
expect(screen.getByText('Test App 2')).toBeInTheDocument()
expect(screen.getByText('Test App 1'))!.toBeInTheDocument()
expect(screen.getByText('Test App 2'))!.toBeInTheDocument()
})
it('should render with all filter options visible', () => {
renderList()
expect(screen.getByRole('textbox')).toBeInTheDocument()
expect(screen.getByText('common.tag.placeholder')).toBeInTheDocument()
expect(screen.getByText('app.showMyCreatedAppsOnly')).toBeInTheDocument()
expect(screen.getByRole('textbox'))!.toBeInTheDocument()
expect(screen.getByText('common.tag.placeholder'))!.toBeInTheDocument()
expect(screen.getByText('app.showMyCreatedAppsOnly'))!.toBeInTheDocument()
})
})
describe('Dragging State', () => {
it('should show drop hint when DSL feature is enabled for editors', () => {
renderList()
expect(screen.getByText('app.newApp.dropDSLToCreateApp')).toBeInTheDocument()
expect(screen.getByText('app.newApp.dropDSLToCreateApp'))!.toBeInTheDocument()
})
it('should render dragging state overlay when dragging', () => {
mockDragging = true
const { container } = renderList()
expect(container).toBeInTheDocument()
expect(container)!.toBeInTheDocument()
})
})
@ -431,12 +431,12 @@ describe('List', () => {
it('should render all app type tabs', () => {
renderList()
expect(screen.getByText('app.types.all')).toBeInTheDocument()
expect(screen.getByText('app.types.workflow')).toBeInTheDocument()
expect(screen.getByText('app.types.advanced')).toBeInTheDocument()
expect(screen.getByText('app.types.chatbot')).toBeInTheDocument()
expect(screen.getByText('app.types.agent')).toBeInTheDocument()
expect(screen.getByText('app.types.completion')).toBeInTheDocument()
expect(screen.getByText('app.types.all'))!.toBeInTheDocument()
expect(screen.getByText('app.types.workflow'))!.toBeInTheDocument()
expect(screen.getByText('app.types.advanced'))!.toBeInTheDocument()
expect(screen.getByText('app.types.chatbot'))!.toBeInTheDocument()
expect(screen.getByText('app.types.agent'))!.toBeInTheDocument()
expect(screen.getByText('app.types.completion'))!.toBeInTheDocument()
})
it('should update URL for each app type tab click', async () => {
@ -454,7 +454,7 @@ describe('List', () => {
onUrlUpdate.mockClear()
fireEvent.click(screen.getByText(text))
await vi.waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
const lastCall = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const lastCall = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(lastCall.searchParams.get('category')).toBe(mode)
}
})
@ -464,22 +464,22 @@ describe('List', () => {
it('should display all app cards from data', () => {
renderList()
expect(screen.getByTestId('app-card-app-1')).toBeInTheDocument()
expect(screen.getByTestId('app-card-app-2')).toBeInTheDocument()
expect(screen.getByTestId('app-card-app-1'))!.toBeInTheDocument()
expect(screen.getByTestId('app-card-app-2'))!.toBeInTheDocument()
})
it('should display app names correctly', () => {
renderList()
expect(screen.getByText('Test App 1')).toBeInTheDocument()
expect(screen.getByText('Test App 2')).toBeInTheDocument()
expect(screen.getByText('Test App 1'))!.toBeInTheDocument()
expect(screen.getByText('Test App 2'))!.toBeInTheDocument()
})
})
describe('Footer Visibility', () => {
it('should render footer when branding is disabled', () => {
renderList()
expect(screen.getByTestId('footer')).toBeInTheDocument()
expect(screen.getByTestId('footer'))!.toBeInTheDocument()
})
})
@ -493,7 +493,7 @@ describe('List', () => {
mockOnDSLFileDropped(mockFile)
})
expect(screen.getByTestId('create-dsl-modal')).toBeInTheDocument()
expect(screen.getByTestId('create-dsl-modal'))!.toBeInTheDocument()
})
it('should close DSL modal when onClose is called', () => {
@ -505,7 +505,7 @@ describe('List', () => {
mockOnDSLFileDropped(mockFile)
})
expect(screen.getByTestId('create-dsl-modal')).toBeInTheDocument()
expect(screen.getByTestId('create-dsl-modal'))!.toBeInTheDocument()
fireEvent.click(screen.getByTestId('close-dsl-modal'))
@ -521,7 +521,7 @@ describe('List', () => {
mockOnDSLFileDropped(mockFile)
})
expect(screen.getByTestId('create-dsl-modal')).toBeInTheDocument()
expect(screen.getByTestId('create-dsl-modal'))!.toBeInTheDocument()
fireEvent.click(screen.getByTestId('success-dsl-modal'))
@ -585,7 +585,7 @@ describe('List', () => {
it('should handle error state in useEffect', () => {
mockServiceState.error = new Error('Test error')
const { container } = renderList()
expect(container).toBeInTheDocument()
expect(container)!.toBeInTheDocument()
})
})
})

View File

@ -115,7 +115,7 @@ const AppCardOperationsMenu: React.FC<AppCardOperationsMenuProps> = ({
await openAsyncWindow(async () => {
const { installed_apps } = await fetchInstalledAppList(app.id)
if (installed_apps?.length > 0)
return `${basePath}/explore/installed/${installed_apps[0].id}`
return `${basePath}/explore/installed/${installed_apps[0]!.id}`
throw new Error('No app found in Explore')
}, {
onError: (err) => {

View File

@ -114,7 +114,7 @@ describe('useAppsQueryState', () => {
})
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(update.searchParams.get('keywords')).toBe('search')
expect(update.options.history).toBe('push')
})
@ -127,7 +127,7 @@ describe('useAppsQueryState', () => {
})
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(update.searchParams.get('tagIDs')).toBe('tag1;tag2')
})
@ -139,7 +139,7 @@ describe('useAppsQueryState', () => {
})
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(update.searchParams.get('isCreatedByMe')).toBe('true')
})
@ -151,7 +151,7 @@ describe('useAppsQueryState', () => {
})
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(update.searchParams.has('keywords')).toBe(false)
})
@ -163,7 +163,7 @@ describe('useAppsQueryState', () => {
})
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(update.searchParams.has('tagIDs')).toBe(false)
})
@ -175,7 +175,7 @@ describe('useAppsQueryState', () => {
})
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]![0]
expect(update.searchParams.has('isCreatedByMe')).toBe(false)
})
})

View File

@ -41,8 +41,8 @@ export const useDSLDragDrop = ({ onDSLFileDropped, containerRef, enabled = true
return
const file = files[0]
if (file.name.toLowerCase().endsWith('.yaml') || file.name.toLowerCase().endsWith('.yml'))
onDSLFileDropped(file)
if (file!.name.toLowerCase().endsWith('.yaml') || file!.name.toLowerCase().endsWith('.yml'))
onDSLFileDropped(file!)
}
useEffect(() => {

View File

@ -151,7 +151,7 @@ const List: FC<Props> = ({
const dynamicMargin = Math.max(100, Math.min(containerHeight * 0.2, 200)) // Clamps to 100-200px range, using 20% of container height as the base value
observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && !isLoading && !isFetchingNextPage && !error && hasMore)
if (entries[0]!.isIntersecting && !isLoading && !isFetchingNextPage && !error && hasMore)
fetchNextPage()
}, {
root: containerRef.current,

View File

@ -85,7 +85,7 @@ describe('amplitude utils', () => {
setUserProperties(properties)
expect(mockIdentify).toHaveBeenCalledTimes(1)
const identifyArg = mockIdentify.mock.calls[0][0] as InstanceType<typeof MockIdentify>
const identifyArg = mockIdentify.mock.calls[0]![0] as InstanceType<typeof MockIdentify>
expect(identifyArg).toBeInstanceOf(MockIdentify)
expect(identifyArg.setCalls).toEqual([
['role', 'owner'],

View File

@ -156,10 +156,10 @@ describe('AppIconPicker', () => {
it('should render emoji and image tabs when upload is enabled', async () => {
renderPicker()
expect(await screen.findByText(/emoji/i)).toBeInTheDocument()
expect(screen.getByText(/image/i)).toBeInTheDocument()
expect(screen.getByText(/cancel/i)).toBeInTheDocument()
expect(screen.getByText(/ok/i)).toBeInTheDocument()
expect(await screen.findByText(/emoji/i))!.toBeInTheDocument()
expect(screen.getByText(/image/i))!.toBeInTheDocument()
expect(screen.getByText(/cancel/i))!.toBeInTheDocument()
expect(screen.getByText(/ok/i))!.toBeInTheDocument()
})
it('should hide the image tab when upload is disabled', () => {
@ -167,7 +167,7 @@ describe('AppIconPicker', () => {
renderPicker()
expect(screen.queryByText(/image/i)).not.toBeInTheDocument()
expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument()
expect(screen.getByPlaceholderText(/search/i))!.toBeInTheDocument()
})
})
@ -184,10 +184,10 @@ describe('AppIconPicker', () => {
renderPicker()
await userEvent.click(screen.getByText(/image/i))
expect(screen.getByText(/drop.*here/i)).toBeInTheDocument()
expect(screen.getByText(/drop.*here/i))!.toBeInTheDocument()
await userEvent.click(screen.getByText(/emoji/i))
expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument()
expect(screen.getByPlaceholderText(/search/i))!.toBeInTheDocument()
})
it('should call onSelect with emoji data after emoji selection', async () => {
@ -251,7 +251,7 @@ describe('AppIconPicker', () => {
fireEvent.change(input, { target: { files: [new File(['png'], 'avatar.png', { type: 'image/png' })] } })
await waitFor(() => {
expect(screen.getByTestId('mock-cropper')).toBeInTheDocument()
expect(screen.getByTestId('mock-cropper'))!.toBeInTheDocument()
})
await userEvent.click(screen.getByTestId('trigger-crop'))
@ -261,7 +261,7 @@ describe('AppIconPicker', () => {
expect(mocks.handleLocalFileUpload).toHaveBeenCalledTimes(1)
})
const uploadedFile = mocks.handleLocalFileUpload.mock.calls[0][0]
const uploadedFile = mocks.handleLocalFileUpload.mock.calls[0]![0]
expect(uploadedFile).toBeInstanceOf(File)
expect(uploadedFile.name).toBe('avatar.png')
expect(uploadedFile.type).toBe('image/png')
@ -291,7 +291,7 @@ describe('AppIconPicker', () => {
await waitFor(() => {
expect(screen.queryByTestId('mock-cropper')).not.toBeInTheDocument()
const preview = screen.queryByTestId('animated-image')
expect(preview).toBeInTheDocument()
expect(preview)!.toBeInTheDocument()
expect(preview?.getAttribute('src')).toContain('blob:mock-url')
})

View File

@ -90,8 +90,8 @@ describe('AudioPlayerManager', () => {
expect(mockAudioPlayerConstructor).toHaveBeenCalledTimes(1)
expect(first).toBe(second)
expect(mockState.instances[0].setCallback).toHaveBeenCalledTimes(1)
expect(mockState.instances[0].setCallback).toHaveBeenCalledWith(secondCallback)
expect(mockState.instances[0]!.setCallback).toHaveBeenCalledTimes(1)
expect(mockState.instances[0]!.setCallback).toHaveBeenCalledWith(secondCallback)
})
it('should cleanup existing player and create a new one when msg id changes', () => {
@ -102,9 +102,9 @@ describe('AudioPlayerManager', () => {
const next = manager.getAudioPlayer('/apps/1/text-to-audio', false, 'msg-2', 'world', 'en-US', callback)
expect(previous.pauseAudio).toHaveBeenCalledTimes(1)
expect(previous.cacheBuffers).toEqual([])
expect(previous.sourceBuffer?.abort).toHaveBeenCalledTimes(1)
expect(previous!.pauseAudio).toHaveBeenCalledTimes(1)
expect(previous!.cacheBuffers).toEqual([])
expect(previous!.sourceBuffer?.abort).toHaveBeenCalledTimes(1)
expect(mockAudioPlayerConstructor).toHaveBeenCalledTimes(2)
expect(next).toBe(mockState.instances[1])
})
@ -114,7 +114,7 @@ describe('AudioPlayerManager', () => {
const callback = vi.fn()
manager.getAudioPlayer('/text-to-audio', false, 'msg-1', 'hello', 'en-US', callback)
const previous = mockState.instances[0]
previous.pauseAudio.mockImplementation(() => {
previous!.pauseAudio.mockImplementation(() => {
throw new Error('cleanup failure')
})
@ -122,7 +122,7 @@ describe('AudioPlayerManager', () => {
manager.getAudioPlayer('/apps/1/text-to-audio', false, 'msg-2', 'world', 'en-US', callback)
}).not.toThrow()
expect(previous.pauseAudio).toHaveBeenCalledTimes(1)
expect(previous!.pauseAudio).toHaveBeenCalledTimes(1)
expect(mockAudioPlayerConstructor).toHaveBeenCalledTimes(2)
})
})
@ -135,8 +135,8 @@ describe('AudioPlayerManager', () => {
manager.resetMsgId('msg-updated')
expect(mockState.instances[0].resetMsgId).toHaveBeenCalledTimes(1)
expect(mockState.instances[0].resetMsgId).toHaveBeenCalledWith('msg-updated')
expect(mockState.instances[0]!.resetMsgId).toHaveBeenCalledTimes(1)
expect(mockState.instances[0]!.resetMsgId).toHaveBeenCalledWith('msg-updated')
})
it('should not throw when resetting message id without an audio player', () => {

View File

@ -241,10 +241,10 @@ describe('AudioPlayer', () => {
expect(player.mediaSource).toBe(mediaSource as unknown as MediaSource)
expect(globalThis.URL.createObjectURL).toHaveBeenCalledTimes(1)
expect(audio.src).toBe('blob:mock-url')
expect(audio.autoplay).toBe(true)
expect(audioContext.createMediaElementSource).toHaveBeenCalledWith(audio)
expect(audioContext.connect).toHaveBeenCalledTimes(1)
expect(audio!.src).toBe('blob:mock-url')
expect(audio!.autoplay).toBe(true)
expect(audioContext!.createMediaElementSource).toHaveBeenCalledWith(audio)
expect(audioContext!.connect).toHaveBeenCalledTimes(1)
})
it('should notify unsupported browser when no MediaSource implementation exists', () => {
@ -254,7 +254,7 @@ describe('AudioPlayer', () => {
const audio = testState.audios[0]
expect(player.mediaSource).toBeNull()
expect(audio.src).toBe('')
expect(audio!.src).toBe('')
expect(mockToastNotify).toHaveBeenCalledTimes(1)
expect(mockToastNotify).toHaveBeenCalledWith(
expect.objectContaining({
@ -271,8 +271,8 @@ describe('AudioPlayer', () => {
const audio = testState.audios[0]
expect(player.mediaSource).not.toBeNull()
expect(audio.disableRemotePlayback).toBe(true)
expect(audio.controls).toBe(true)
expect(audio!.disableRemotePlayback).toBe(true)
expect(audio!.controls).toBe(true)
})
})
@ -282,14 +282,14 @@ describe('AudioPlayer', () => {
const player = new AudioPlayer('/text-to-audio', true, 'msg-1', 'hello', 'en-US', callback)
const audio = testState.audios[0]
audio.emit('play')
audio.emit('ended')
audio.emit('error')
audio.emit('paused')
audio.emit('loaded')
audio.emit('timeupdate')
audio.emit('loadeddate')
audio.emit('canplay')
audio!.emit('play')
audio!.emit('ended')
audio!.emit('error')
audio!.emit('paused')
audio!.emit('loaded')
audio!.emit('timeupdate')
audio!.emit('loadeddate')
audio!.emit('canplay')
expect(player.callback).toBe(callback)
expect(callback).toHaveBeenCalledWith('play')
@ -306,11 +306,11 @@ describe('AudioPlayer', () => {
const player = new AudioPlayer('/text-to-audio', true, 'msg-1', 'hello', 'en-US', vi.fn())
const mediaSource = testState.mediaSources[0]
mediaSource.emit('sourceopen')
mediaSource.emit('sourceopen')
mediaSource!.emit('sourceopen')
mediaSource!.emit('sourceopen')
expect(mediaSource.addSourceBuffer).toHaveBeenCalledTimes(1)
expect(player.sourceBuffer).toBe(mediaSource.sourceBuffer)
expect(mediaSource!.addSourceBuffer).toHaveBeenCalledTimes(1)
expect(player.sourceBuffer).toBe(mediaSource!.sourceBuffer)
})
})
@ -366,12 +366,12 @@ describe('AudioPlayer', () => {
const audioContext = testState.audioContexts[0]
player.isLoadData = true
audioContext.state = 'suspended'
audioContext!.state = 'suspended'
player.playAudio()
await Promise.resolve()
expect(audioContext.resume).toHaveBeenCalledTimes(1)
expect(audio.play).toHaveBeenCalledTimes(1)
expect(audioContext!.resume).toHaveBeenCalledTimes(1)
expect(audio!.play).toHaveBeenCalledTimes(1)
expect(callback).toHaveBeenCalledWith('play')
})
@ -382,11 +382,11 @@ describe('AudioPlayer', () => {
const audioContext = testState.audioContexts[0]
player.isLoadData = true
audioContext.state = 'running'
audio.ended = true
audioContext!.state = 'running'
audio!.ended = true
player.playAudio()
expect(audio.play).toHaveBeenCalledTimes(1)
expect(audio!.play).toHaveBeenCalledTimes(1)
expect(callback).toHaveBeenCalledWith('play')
})
@ -397,11 +397,11 @@ describe('AudioPlayer', () => {
const audioContext = testState.audioContexts[0]
player.isLoadData = true
audioContext.state = 'running'
audio.ended = false
audioContext!.state = 'running'
audio!.ended = false
player.playAudio()
expect(audio.play).not.toHaveBeenCalled()
expect(audio!.play).not.toHaveBeenCalled()
expect(callback).toHaveBeenCalledWith('play')
})
@ -427,8 +427,8 @@ describe('AudioPlayer', () => {
player.pauseAudio()
expect(callback).toHaveBeenCalledWith('paused')
expect(audio.pause).toHaveBeenCalledTimes(1)
expect(audioContext.suspend).toHaveBeenCalledTimes(1)
expect(audio!.pause).toHaveBeenCalledTimes(1)
expect(audioContext!.suspend).toHaveBeenCalledTimes(1)
})
})
@ -453,7 +453,7 @@ describe('AudioPlayer', () => {
expect(player.isLoadData).toBe(false)
expect(player.cacheBuffers).toHaveLength(0)
expect(mediaSource.endOfStream).toHaveBeenCalledTimes(1)
expect(mediaSource!.endOfStream).toHaveBeenCalledTimes(1)
expect(callback).not.toHaveBeenCalledWith('play')
}
finally {
@ -469,19 +469,19 @@ describe('AudioPlayer', () => {
const mediaSource = testState.mediaSources[0]
const audioBase64 = Buffer.from('hello').toString('base64')
mediaSource.emit('sourceopen')
audio.paused = true
mediaSource!.emit('sourceopen')
audio!.paused = true
await player.playAudioWithAudio(audioBase64, true)
await Promise.resolve()
expect(player.isLoadData).toBe(true)
expect(player.cacheBuffers).toHaveLength(0)
expect(mediaSource.sourceBuffer.appendBuffer).toHaveBeenCalledTimes(1)
const appendedAudioData = mediaSource.sourceBuffer.appendBuffer.mock.calls[0][0]
expect(mediaSource!.sourceBuffer.appendBuffer).toHaveBeenCalledTimes(1)
const appendedAudioData = mediaSource!.sourceBuffer.appendBuffer.mock.calls[0]![0]
expect(appendedAudioData).toBeInstanceOf(ArrayBuffer)
expect(appendedAudioData.byteLength).toBeGreaterThan(0)
expect(audioContext.resume).toHaveBeenCalledTimes(1)
expect(audio.play).toHaveBeenCalledTimes(1)
expect(audioContext!.resume).toHaveBeenCalledTimes(1)
expect(audio!.play).toHaveBeenCalledTimes(1)
expect(callback).toHaveBeenCalledWith('play')
})
@ -494,8 +494,8 @@ describe('AudioPlayer', () => {
await player.playAudioWithAudio(Buffer.from('hello').toString('base64'), false)
expect(player.isLoadData).toBe(false)
expect(audioContext.resume).not.toHaveBeenCalled()
expect(audio.play).not.toHaveBeenCalled()
expect(audioContext!.resume).not.toHaveBeenCalled()
expect(audio!.play).not.toHaveBeenCalled()
expect(callback).not.toHaveBeenCalledWith('play')
})
@ -504,11 +504,11 @@ describe('AudioPlayer', () => {
const player = new AudioPlayer('/text-to-audio', true, 'msg-1', 'hello', 'en-US', callback)
const audio = testState.audios[0]
audio.paused = false
audio.ended = true
audio!.paused = false
audio!.ended = true
await player.playAudioWithAudio(Buffer.from('hello').toString('base64'), true)
expect(audio.play).toHaveBeenCalledTimes(1)
expect(audio!.play).toHaveBeenCalledTimes(1)
expect(callback).toHaveBeenCalledWith('play')
})
@ -517,12 +517,12 @@ describe('AudioPlayer', () => {
const player = new AudioPlayer('/text-to-audio', true, 'msg-1', 'hello', 'en-US', callback)
const audio = testState.audios[0]
audio.paused = false
audio.ended = false
audio.played = {}
audio!.paused = false
audio!.ended = false
audio!.played = {}
await player.playAudioWithAudio(Buffer.from('hello').toString('base64'), true)
expect(audio.play).not.toHaveBeenCalled()
expect(audio!.play).not.toHaveBeenCalled()
expect(callback).not.toHaveBeenCalledWith('play')
})
@ -531,12 +531,12 @@ describe('AudioPlayer', () => {
const player = new AudioPlayer('/text-to-audio', true, 'msg-1', 'hello', 'en-US', callback)
const audio = testState.audios[0]
audio.paused = false
audio.ended = false
audio.played = null
audio!.paused = false
audio!.ended = false
audio!.played = null
await player.playAudioWithAudio(Buffer.from('hello').toString('base64'), true)
expect(audio.play).toHaveBeenCalledTimes(1)
expect(audio!.play).toHaveBeenCalledTimes(1)
expect(callback).toHaveBeenCalledWith('play')
})
})
@ -567,8 +567,8 @@ describe('AudioPlayer', () => {
it('should queue incoming buffer when source buffer is updating', () => {
const player = new AudioPlayer('/text-to-audio', true, 'msg-1', 'hello', 'en-US', null)
const mediaSource = testState.mediaSources[0]
mediaSource.emit('sourceopen')
mediaSource.sourceBuffer.updating = true
mediaSource!.emit('sourceopen')
mediaSource!.sourceBuffer.updating = true
; (player as unknown as { receiveAudioData: (data: Uint8Array) => void }).receiveAudioData(new Uint8Array([1, 2, 3]))
@ -578,16 +578,16 @@ describe('AudioPlayer', () => {
it('should append previously queued buffer before new one when source buffer is idle', () => {
const player = new AudioPlayer('/text-to-audio', true, 'msg-1', 'hello', 'en-US', null)
const mediaSource = testState.mediaSources[0]
mediaSource.emit('sourceopen')
mediaSource!.emit('sourceopen')
const existingBuffer = new ArrayBuffer(2)
player.cacheBuffers = [existingBuffer]
mediaSource.sourceBuffer.updating = false
mediaSource!.sourceBuffer.updating = false
; (player as unknown as { receiveAudioData: (data: Uint8Array) => void }).receiveAudioData(new Uint8Array([9]))
expect(mediaSource.sourceBuffer.appendBuffer).toHaveBeenCalledTimes(1)
expect(mediaSource.sourceBuffer.appendBuffer).toHaveBeenCalledWith(existingBuffer)
expect(mediaSource!.sourceBuffer.appendBuffer).toHaveBeenCalledTimes(1)
expect(mediaSource!.sourceBuffer.appendBuffer).toHaveBeenCalledWith(existingBuffer)
expect(player.cacheBuffers.length).toBe(1)
})
@ -595,15 +595,15 @@ describe('AudioPlayer', () => {
vi.useFakeTimers()
const player = new AudioPlayer('/text-to-audio', true, 'msg-1', 'hello', 'en-US', null)
const mediaSource = testState.mediaSources[0]
mediaSource.emit('sourceopen')
mediaSource.sourceBuffer.updating = false
mediaSource!.emit('sourceopen')
mediaSource!.sourceBuffer.updating = false
player.cacheBuffers = [new ArrayBuffer(3)]
; (player as unknown as { finishStream: () => void }).finishStream()
vi.advanceTimersByTime(50)
expect(mediaSource.sourceBuffer.appendBuffer).toHaveBeenCalledTimes(1)
expect(mediaSource.endOfStream).toHaveBeenCalledTimes(1)
expect(mediaSource!.sourceBuffer.appendBuffer).toHaveBeenCalledTimes(1)
expect(mediaSource!.endOfStream).toHaveBeenCalledTimes(1)
vi.useRealTimers()
})
})

View File

@ -32,7 +32,7 @@ describe('AudioBtn', () => {
const hoverAndCheckTooltip = async (expectedText: string) => {
await userEvent.hover(getButton())
expect(await screen.findByText(expectedText)).toBeInTheDocument()
expect(await screen.findByText(expectedText))!.toBeInTheDocument()
}
const getLatestAudioCallback = () => {
@ -64,7 +64,7 @@ describe('AudioBtn', () => {
it('should render button with play tooltip by default', async () => {
render(<AudioBtn value="hello" />)
expect(getButton()).toBeInTheDocument()
expect(getButton())!.toBeInTheDocument()
expect(getButton()).not.toBeDisabled()
await hoverAndCheckTooltip('play')
})
@ -73,7 +73,7 @@ describe('AudioBtn', () => {
const { container } = render(<AudioBtn value="hello" className="custom-wrapper" />)
const wrapper = container.firstElementChild
expect(wrapper).toHaveClass('custom-wrapper')
expect(wrapper)!.toHaveClass('custom-wrapper')
})
})
@ -87,8 +87,8 @@ describe('AudioBtn', () => {
await waitFor(() => expect(mockGetAudioPlayer).toHaveBeenCalled())
const call = mockGetAudioPlayer.mock.calls[0]
expect(call[0]).toBe('/text-to-audio')
expect(call[1]).toBe(true)
expect(call![0]).toBe('/text-to-audio')
expect(call![1]).toBe(true)
})
it('should call app endpoint when appId exists', async () => {
@ -100,8 +100,8 @@ describe('AudioBtn', () => {
await waitFor(() => expect(mockGetAudioPlayer).toHaveBeenCalled())
const call = mockGetAudioPlayer.mock.calls[0]
expect(call[0]).toBe('/apps/123/text-to-audio')
expect(call[1]).toBe(false)
expect(call![0]).toBe('/apps/123/text-to-audio')
expect(call![1]).toBe(false)
})
it('should call installed app endpoint for explore installed routes', async () => {
@ -113,8 +113,8 @@ describe('AudioBtn', () => {
await waitFor(() => expect(mockGetAudioPlayer).toHaveBeenCalled())
const call = mockGetAudioPlayer.mock.calls[0]
expect(call[0]).toBe('/installed-apps/456/text-to-audio')
expect(call[1]).toBe(false)
expect(call![0]).toBe('/installed-apps/456/text-to-audio')
expect(call![1]).toBe(false)
})
})
@ -126,9 +126,9 @@ describe('AudioBtn', () => {
await waitFor(() => {
expect(mockPlayAudio).toHaveBeenCalledTimes(1)
expect(getButton()).toBeDisabled()
expect(getButton())!.toBeDisabled()
})
expect(screen.getByRole('status')).toBeInTheDocument()
expect(screen.getByRole('status'))!.toBeInTheDocument()
await hoverAndCheckTooltip('loading')
})
@ -159,7 +159,7 @@ describe('AudioBtn', () => {
})
await hoverAndCheckTooltip('loading')
expect(getButton()).toBeDisabled()
expect(getButton())!.toBeDisabled()
})
it.each(['ended', 'paused', 'error'])('should return to play tooltip when %s event is received', async (event) => {
@ -183,9 +183,9 @@ describe('AudioBtn', () => {
await waitFor(() => expect(mockGetAudioPlayer).toHaveBeenCalled())
const call = mockGetAudioPlayer.mock.calls[0]
expect(call[2]).toBe('msg-1')
expect(call[3]).toBe('hello')
expect(call[4]).toBe('en-US')
expect(call![2]).toBe('msg-1')
expect(call![3]).toBe('hello')
expect(call![4]).toBe('en-US')
})
it('should keep empty route when neither token nor appId is present', async () => {
@ -194,9 +194,9 @@ describe('AudioBtn', () => {
await waitFor(() => expect(mockGetAudioPlayer).toHaveBeenCalled())
const call = mockGetAudioPlayer.mock.calls[0]
expect(call[0]).toBe('')
expect(call[1]).toBe(false)
expect(call[3]).toBeUndefined()
expect(call![0]).toBe('')
expect(call![1]).toBe(false)
expect(call![3]).toBeUndefined()
})
})
})

View File

@ -95,7 +95,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = ({ src, srcs }) => {
for (let i = 0; i < samples; i++) {
let sum = 0
for (let j = 0; j < blockSize; j++)
sum += Math.abs(channelData[i * blockSize + j])
sum += Math.abs(channelData[i * blockSize + j]!)
// Apply nonlinear scaling to enhance small amplitudes
waveformData.push((sum / blockSize) * 5)
}
@ -145,7 +145,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = ({ src, srcs }) => {
e.preventDefault()
const getClientX = (event: React.MouseEvent | React.TouchEvent): number => {
if ('touches' in event)
return event.touches[0].clientX
return event.touches[0]!.clientX
return event.clientX
}
const updateProgress = (clientX: number) => {

View File

@ -47,7 +47,7 @@ async function advanceWaveformTimer() {
type ReactEventHandler = ((...args: any[]) => void) | undefined
function getReactProps<T extends Element>(el: T): Record<string, ReactEventHandler> {
const key = Object.keys(el).find(k => k.startsWith('__reactProps$'))
return key ? (el as unknown as Record<string, Record<string, ReactEventHandler>>)[key] : {}
return key ? (el as unknown as Record<string, Record<string, ReactEventHandler>>)[key]! : {}
}
// ─── Setup / teardown ─────────────────────────────────────────────────────────
@ -77,8 +77,8 @@ describe('AudioPlayer — rendering', () => {
it('should render the play button and audio element when given a src', () => {
render(<AudioPlayer src="https://example.com/a.mp3" />)
expect(screen.getByTestId('play-pause-btn')).toBeInTheDocument()
expect(document.querySelector('audio')).toBeInTheDocument()
expect(screen.getByTestId('play-pause-btn'))!.toBeInTheDocument()
expect(document.querySelector('audio'))!.toBeInTheDocument()
expect(document.querySelector('audio')?.getAttribute('src')).toBe('https://example.com/a.mp3')
})
@ -93,7 +93,7 @@ describe('AudioPlayer — rendering', () => {
it('should render without crashing when no props are supplied', () => {
render(<AudioPlayer />)
expect(screen.getByTestId('play-pause-btn')).toBeInTheDocument()
expect(screen.getByTestId('play-pause-btn'))!.toBeInTheDocument()
})
})
@ -129,14 +129,14 @@ describe('AudioPlayer — play/pause', () => {
render(<AudioPlayer src="https://example.com/a.mp3" />)
const btn = screen.getByTestId('play-pause-btn')
expect(btn.querySelector('.i-ri-play-large-fill')).toBeInTheDocument()
expect(btn.querySelector('.i-ri-play-large-fill'))!.toBeInTheDocument()
expect(btn.querySelector('.i-ri-pause-circle-fill')).not.toBeInTheDocument()
await act(async () => {
fireEvent.click(btn)
})
expect(btn.querySelector('.i-ri-pause-circle-fill')).toBeInTheDocument()
expect(btn.querySelector('.i-ri-pause-circle-fill'))!.toBeInTheDocument()
expect(btn.querySelector('.i-ri-play-large-fill')).not.toBeInTheDocument()
})
@ -147,14 +147,14 @@ describe('AudioPlayer — play/pause', () => {
await act(async () => {
fireEvent.click(btn)
})
expect(btn.querySelector('.i-ri-pause-circle-fill')).toBeInTheDocument()
expect(btn.querySelector('.i-ri-pause-circle-fill'))!.toBeInTheDocument()
const audio = document.querySelector('audio') as HTMLAudioElement
await act(async () => {
audio.dispatchEvent(new Event('ended'))
})
expect(btn.querySelector('.i-ri-play-large-fill')).toBeInTheDocument()
expect(btn.querySelector('.i-ri-play-large-fill'))!.toBeInTheDocument()
})
it('should disable the play button when an audio error occurs', async () => {
@ -165,7 +165,7 @@ describe('AudioPlayer — play/pause', () => {
audio.dispatchEvent(new Event('error'))
})
expect(screen.getByTestId('play-pause-btn')).toBeDisabled()
expect(screen.getByTestId('play-pause-btn'))!.toBeDisabled()
})
})
@ -181,7 +181,7 @@ describe('AudioPlayer — audio events', () => {
audio.dispatchEvent(new Event('loadedmetadata'))
})
expect(screen.getByText('1:30')).toBeInTheDocument()
expect(screen.getByText('1:30'))!.toBeInTheDocument()
})
it('should update bufferedTime on progress event', async () => {
@ -216,7 +216,7 @@ describe('AudioPlayer — audio events', () => {
audio.dispatchEvent(new Event('error'))
})
expect(screen.getByTestId('play-pause-btn')).toBeDisabled()
expect(screen.getByTestId('play-pause-btn'))!.toBeDisabled()
})
})
@ -230,7 +230,7 @@ describe('AudioPlayer — waveform generation', () => {
render(<AudioPlayer src="https://cdn.example/audio.mp3" />)
await advanceWaveformTimer()
expect(screen.getByTestId('waveform-canvas')).toBeInTheDocument()
expect(screen.getByTestId('waveform-canvas'))!.toBeInTheDocument()
})
it('should use fallback random waveform when fetch returns not-ok', async () => {
@ -240,7 +240,7 @@ describe('AudioPlayer — waveform generation', () => {
render(<AudioPlayer src="https://cdn.example/audio.mp3" />)
await advanceWaveformTimer()
expect(screen.getByTestId('waveform-canvas')).toBeInTheDocument()
expect(screen.getByTestId('waveform-canvas'))!.toBeInTheDocument()
})
it('should use fallback waveform when decodeAudioData rejects', async () => {
@ -257,7 +257,7 @@ describe('AudioPlayer — waveform generation', () => {
render(<AudioPlayer src="https://cdn.example/audio.mp3" />)
await advanceWaveformTimer()
expect(screen.getByTestId('waveform-canvas')).toBeInTheDocument()
expect(screen.getByTestId('waveform-canvas'))!.toBeInTheDocument()
})
it('should show Toast when AudioContext is not available', async () => {
@ -276,7 +276,7 @@ describe('AudioPlayer — waveform generation', () => {
render(<AudioPlayer srcs={['blob:something']} />)
await advanceWaveformTimer()
expect(screen.getByTestId('play-pause-btn')).toBeDisabled()
expect(screen.getByTestId('play-pause-btn'))!.toBeDisabled()
})
it('should not trigger waveform generation when no src or srcs provided', async () => {
@ -305,7 +305,7 @@ describe('AudioPlayer — waveform generation', () => {
render(<AudioPlayer src="https://cdn.example/audio.mp3" />)
await advanceWaveformTimer()
expect(screen.getByTestId('waveform-canvas')).toBeInTheDocument()
expect(screen.getByTestId('waveform-canvas'))!.toBeInTheDocument()
})
it('should use webkitAudioContext when AudioContext is unavailable', async () => {
@ -316,7 +316,7 @@ describe('AudioPlayer — waveform generation', () => {
render(<AudioPlayer src="https://cdn.example/audio.mp3" />)
await advanceWaveformTimer()
expect(screen.getByTestId('waveform-canvas')).toBeInTheDocument()
expect(screen.getByTestId('waveform-canvas'))!.toBeInTheDocument()
})
})
@ -425,7 +425,7 @@ describe('AudioPlayer — missing coverage', () => {
HTMLCanvasElement.prototype.getContext = vi.fn().mockReturnValue(null)
render(<AudioPlayer src="https://example.com/audio.mp3" />)
expect(screen.getByTestId('waveform-canvas')).toBeInTheDocument()
expect(screen.getByTestId('waveform-canvas'))!.toBeInTheDocument()
HTMLCanvasElement.prototype.getContext = originalGetContext
})
@ -535,7 +535,7 @@ describe('AudioPlayer — missing coverage', () => {
fireEvent.click(btn)
})
expect(btn).toBeDisabled()
expect(btn)!.toBeDisabled()
expect(HTMLMediaElement.prototype.play).not.toHaveBeenCalled()
expect(toastSpy).not.toHaveBeenCalled()
toastSpy.mockRestore()
@ -606,7 +606,7 @@ describe('AudioPlayer — additional branch coverage', () => {
audio.dispatchEvent(new Event('error'))
})
expect(screen.queryByTestId('play-pause-btn')).toBeDisabled()
expect(screen.queryByTestId('play-pause-btn'))!.toBeDisabled()
})
it('should update current time on timeupdate event', async () => {
@ -632,7 +632,7 @@ describe('AudioPlayer — additional branch coverage', () => {
fireEvent.click(btn)
})
expect(btn).toBeDisabled()
expect(btn)!.toBeDisabled()
expect(HTMLMediaElement.prototype.play).not.toHaveBeenCalled()
expect(toastSpy).not.toHaveBeenCalled()
toastSpy.mockRestore()
@ -654,7 +654,7 @@ describe('AudioPlayer — additional branch coverage', () => {
})
await advanceWaveformTimer()
expect(screen.getByTestId('waveform-canvas')).toBeInTheDocument()
expect(screen.getByTestId('waveform-canvas'))!.toBeInTheDocument()
})
it('should handle missing canvas/audio in handleCanvasInteraction/handleMouseMove', async () => {
@ -683,7 +683,7 @@ describe('AudioPlayer — additional branch coverage', () => {
audio.dispatchEvent(new Event('timeupdate'))
})
expect(canvas).toBeInTheDocument()
expect(canvas)!.toBeInTheDocument()
})
it('should hit null-ref guards in canvas handlers after unmount', async () => {
@ -710,6 +710,6 @@ describe('AudioPlayer — additional branch coverage', () => {
fireEvent.mouseMove(canvas, { clientX: 180 }) // time near 90, outside 0-10
})
expect(canvas).toBeInTheDocument()
expect(canvas)!.toBeInTheDocument()
})
})

View File

@ -75,7 +75,7 @@ const BlockInput: FC<IBlockInputProps> = ({
return (
<VarHighlight
key={`var-${index}`}
name={variableMatch[1]}
name={variableMatch[1]!}
/>
)
}

View File

@ -99,10 +99,10 @@ describe('Carousel', () => {
it('should render region and slides when used with content and items', () => {
renderCarouselWithControls()
expect(screen.getByRole('region')).toHaveAttribute('aria-roledescription', 'carousel')
expect(screen.getByTestId('carousel-content')).toHaveClass('flex')
expect(screen.getByRole('region'))!.toHaveAttribute('aria-roledescription', 'carousel')
expect(screen.getByTestId('carousel-content'))!.toHaveClass('flex')
screen.getAllByRole('group').forEach((slide) => {
expect(slide).toHaveAttribute('aria-roledescription', 'slide')
expect(slide)!.toHaveAttribute('aria-roledescription', 'slide')
})
})
})
@ -130,7 +130,7 @@ describe('Carousel', () => {
{ axis: 'y' },
undefined,
)
expect(screen.getByTestId('carousel-content')).toHaveClass('flex-col')
expect(screen.getByTestId('carousel-content'))!.toHaveClass('flex-col')
})
})
@ -171,7 +171,7 @@ describe('Carousel', () => {
renderCarouselWithControls()
const dots = screen.getAllByRole('button', { name: 'Dot' })
fireEvent.click(dots[2])
fireEvent.click(dots[2]!)
expect(mockApi.scrollTo).toHaveBeenCalledWith(2)
})
@ -191,9 +191,9 @@ describe('Carousel', () => {
})
const dots = screen.getAllByRole('button', { name: 'Dot' })
expect(screen.getByRole('button', { name: 'Prev' })).toBeEnabled()
expect(screen.getByRole('button', { name: 'Next' })).toBeEnabled()
expect(dots[2]).toHaveAttribute('data-state', 'active')
expect(screen.getByRole('button', { name: 'Prev' }))!.toBeEnabled()
expect(screen.getByRole('button', { name: 'Next' }))!.toBeEnabled()
expect(dots[2])!.toHaveAttribute('data-state', 'active')
})
it('should subscribe to embla events and unsubscribe from select on unmount', () => {
@ -232,8 +232,8 @@ describe('Carousel', () => {
renderCarouselWithControls()
expect(screen.getByRole('button', { name: 'Prev' })).toBeDisabled()
expect(screen.getByRole('button', { name: 'Next' })).toBeDisabled()
expect(screen.getByRole('button', { name: 'Prev' }))!.toBeDisabled()
expect(screen.getByRole('button', { name: 'Next' }))!.toBeDisabled()
expect(screen.queryByRole('button', { name: 'Dot' })).not.toBeInTheDocument()
})

View File

@ -418,11 +418,11 @@ describe('chat utils - url params and answer helpers', () => {
const tree = buildChatItemTree(list)
expect(tree.length).toBe(1)
expect(tree[0].id).toBe('q1')
expect(tree[0].children?.[0].id).toBe('a1')
expect(tree[0].children?.[0].children?.[0].id).toBe('q2')
expect(tree[0].children?.[0].children?.[0].children?.[0].id).toBe('a2')
expect(tree[0].children?.[0].children?.[0].children?.[0].siblingIndex).toBe(0)
expect(tree[0]!.id).toBe('q1')
expect(tree[0]!.children?.[0]!.id).toBe('a1')
expect(tree[0]!.children?.[0]!.children?.[0]!.id).toBe('q2')
expect(tree[0]!.children?.[0]!.children?.[0]!.children?.[0]!.id).toBe('a2')
expect(tree[0]!.children?.[0]!.children?.[0]!.children?.[0]!.siblingIndex).toBe(0)
})
it('buildChatItemTree builds nested tree based on parentMessageId', () => {
@ -439,23 +439,23 @@ describe('chat utils - url params and answer helpers', () => {
const tree = buildChatItemTree(list)
expect(tree.length).toBe(2)
expect(tree[0].id).toBe('q1')
expect(tree[1].id).toBe('q4')
expect(tree[0]!.id).toBe('q1')
expect(tree[1]!.id).toBe('q4')
const a1 = tree[0].children![0]
expect(a1.id).toBe('a1')
expect(a1.children?.length).toBe(2)
expect(a1.children![0].id).toBe('q2')
expect(a1.children![1].id).toBe('q3')
expect(a1.children![0].children![0].siblingIndex).toBe(0)
expect(a1.children![1].children![0].siblingIndex).toBe(1)
const a1 = tree[0]!.children![0]
expect(a1!.id).toBe('a1')
expect(a1!.children?.length).toBe(2)
expect(a1!.children![0]!.id).toBe('q2')
expect(a1!.children![1]!.id).toBe('q3')
expect(a1!.children![0]!.children![0]!.siblingIndex).toBe(0)
expect(a1!.children![1]!.children![0]!.siblingIndex).toBe(1)
})
it('getThreadMessages node without children', () => {
const tree = [{ id: 'q1', isAnswer: false }]
const thread = getThreadMessages(tree as unknown as ChatItemInTree[], 'q1')
expect(thread.length).toBe(1)
expect(thread[0].id).toBe('q1')
expect(thread[0]!.id).toBe('q1')
})
it('getThreadMessages target not found', () => {
@ -494,8 +494,8 @@ describe('chat utils - url params and answer helpers', () => {
const thread = getThreadMessages(tree as unknown as ChatItemInTree[])
expect(thread.length).toBe(4)
expect(thread.map(t => t.id)).toEqual(['q1', 'a1', 'q2', 'a2'])
expect(thread[1].siblingCount).toBe(1)
expect(thread[3].siblingCount).toBe(1)
expect(thread[1]!.siblingCount).toBe(1)
expect(thread[3]!.siblingCount).toBe(1)
})
it('getThreadMessages to specific target', () => {
@ -531,8 +531,8 @@ describe('chat utils - url params and answer helpers', () => {
const thread = getThreadMessages(tree as unknown as ChatItemInTree[], 'a3')
expect(thread.length).toBe(4)
expect(thread.map(t => t.id)).toEqual(['q1', 'a1', 'q3', 'a3'])
expect(thread[3].prevSibling).toBe('a2')
expect(thread[3].nextSibling).toBeUndefined()
expect(thread[3]!.prevSibling).toBe('a2')
expect(thread[3]!.nextSibling).toBeUndefined()
})
it('getThreadMessages targetNode has descendants', () => {
@ -568,7 +568,7 @@ describe('chat utils - url params and answer helpers', () => {
const thread = getThreadMessages(tree as unknown as ChatItemInTree[], 'a1')
expect(thread.length).toBe(4)
expect(thread.map(t => t.id)).toEqual(['q1', 'a1', 'q3', 'a3'])
expect(thread[3].prevSibling).toBe('a2')
expect(thread[3]!.prevSibling).toBe('a2')
})
})
})

Some files were not shown because too many files have changed in this diff Show More