mirror of
https://github.com/langgenius/dify.git
synced 2026-04-17 03:16:33 +08:00
chore: enable noUncheckedIndexedAccess (#35178)
This commit is contained in:
parent
e507675860
commit
abb84f1c38
@ -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)
|
||||
20
packages/migrate-no-unchecked-indexed-access/package.json
Normal file
20
packages/migrate-no-unchecked-indexed-access/package.json
Normal 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:"
|
||||
}
|
||||
}
|
||||
45
packages/migrate-no-unchecked-indexed-access/src/cli.ts
Normal file
45
packages/migrate-no-unchecked-indexed-access/src/cli.ts
Normal 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)
|
||||
File diff suppressed because it is too large
Load Diff
@ -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()
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
17
packages/migrate-no-unchecked-indexed-access/vite.config.ts
Normal file
17
packages/migrate-no-unchecked-indexed-access/vite.config.ts
Normal 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
16
pnpm-lock.yaml
generated
@ -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':
|
||||
|
||||
@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
@ -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('')
|
||||
})
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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>
|
||||
)
|
||||
})}
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@ -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(() => {
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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' },
|
||||
|
||||
@ -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' }))
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -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')
|
||||
|
||||
|
||||
@ -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' }])
|
||||
})
|
||||
|
||||
|
||||
@ -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}}', [])
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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]
|
||||
|
||||
|
||||
@ -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' }),
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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]!,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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,
|
||||
}))
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@ -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()
|
||||
})
|
||||
|
||||
@ -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)')
|
||||
})
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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,
|
||||
})
|
||||
|
||||
|
||||
@ -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'] })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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'))
|
||||
|
||||
|
||||
@ -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)
|
||||
})
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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'),
|
||||
}
|
||||
: {}),
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 }
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
@ -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>
|
||||
)
|
||||
|
||||
@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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()
|
||||
})
|
||||
|
||||
@ -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'),
|
||||
}
|
||||
: {}),
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
@ -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(() => {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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'],
|
||||
|
||||
@ -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')
|
||||
})
|
||||
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@ -75,7 +75,7 @@ const BlockInput: FC<IBlockInputProps> = ({
|
||||
return (
|
||||
<VarHighlight
|
||||
key={`var-${index}`}
|
||||
name={variableMatch[1]}
|
||||
name={variableMatch[1]!}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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()
|
||||
})
|
||||
|
||||
|
||||
@ -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
Loading…
Reference in New Issue
Block a user