chore(web): new lint setup (#30020)

Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
Stephen Zhou
2025-12-23 16:58:55 +08:00
committed by GitHub
parent 9701a2994b
commit f2842da397
3356 changed files with 85046 additions and 81278 deletions

View File

@@ -12,43 +12,43 @@ describe('app-redirection', () => {
* - App mode (workflow, advanced-chat, chat, completion, agent-chat)
*/
describe('getRedirectionPath', () => {
test('returns overview path when user is not editor', () => {
it('returns overview path when user is not editor', () => {
const app = { id: 'app-123', mode: AppModeEnum.CHAT }
const result = getRedirectionPath(false, app)
expect(result).toBe('/app/app-123/overview')
})
test('returns workflow path for workflow mode when user is editor', () => {
it('returns workflow path for workflow mode when user is editor', () => {
const app = { id: 'app-123', mode: AppModeEnum.WORKFLOW }
const result = getRedirectionPath(true, app)
expect(result).toBe('/app/app-123/workflow')
})
test('returns workflow path for advanced-chat mode when user is editor', () => {
it('returns workflow path for advanced-chat mode when user is editor', () => {
const app = { id: 'app-123', mode: AppModeEnum.ADVANCED_CHAT }
const result = getRedirectionPath(true, app)
expect(result).toBe('/app/app-123/workflow')
})
test('returns configuration path for chat mode when user is editor', () => {
it('returns configuration path for chat mode when user is editor', () => {
const app = { id: 'app-123', mode: AppModeEnum.CHAT }
const result = getRedirectionPath(true, app)
expect(result).toBe('/app/app-123/configuration')
})
test('returns configuration path for completion mode when user is editor', () => {
it('returns configuration path for completion mode when user is editor', () => {
const app = { id: 'app-123', mode: AppModeEnum.COMPLETION }
const result = getRedirectionPath(true, app)
expect(result).toBe('/app/app-123/configuration')
})
test('returns configuration path for agent-chat mode when user is editor', () => {
it('returns configuration path for agent-chat mode when user is editor', () => {
const app = { id: 'app-456', mode: AppModeEnum.AGENT_CHAT }
const result = getRedirectionPath(true, app)
expect(result).toBe('/app/app-456/configuration')
})
test('handles different app IDs', () => {
it('handles different app IDs', () => {
const app1 = { id: 'abc-123', mode: AppModeEnum.CHAT }
const app2 = { id: 'xyz-789', mode: AppModeEnum.WORKFLOW }
@@ -64,7 +64,7 @@ describe('app-redirection', () => {
/**
* Tests that the redirection function is called with the correct path
*/
test('calls redirection function with correct path for non-editor', () => {
it('calls redirection function with correct path for non-editor', () => {
const app = { id: 'app-123', mode: AppModeEnum.CHAT }
const mockRedirect = vi.fn()
@@ -74,7 +74,7 @@ describe('app-redirection', () => {
expect(mockRedirect).toHaveBeenCalledTimes(1)
})
test('calls redirection function with workflow path for editor', () => {
it('calls redirection function with workflow path for editor', () => {
const app = { id: 'app-123', mode: AppModeEnum.WORKFLOW }
const mockRedirect = vi.fn()
@@ -84,7 +84,7 @@ describe('app-redirection', () => {
expect(mockRedirect).toHaveBeenCalledTimes(1)
})
test('calls redirection function with configuration path for chat mode editor', () => {
it('calls redirection function with configuration path for chat mode editor', () => {
const app = { id: 'app-123', mode: AppModeEnum.CHAT }
const mockRedirect = vi.fn()
@@ -94,7 +94,7 @@ describe('app-redirection', () => {
expect(mockRedirect).toHaveBeenCalledTimes(1)
})
test('works with different redirection functions', () => {
it('works with different redirection functions', () => {
const app = { id: 'app-123', mode: AppModeEnum.WORKFLOW }
const paths: string[] = []
const customRedirect = (path: string) => paths.push(path)

View File

@@ -13,7 +13,7 @@ describe('classnames', () => {
* - Falsy value filtering
* - Object-based conditional classes
*/
test('classnames libs feature', () => {
it('classnames libs feature', () => {
expect(cn('foo')).toBe('foo')
expect(cn('foo', 'bar')).toBe('foo bar')
expect(cn(['foo', 'bar'])).toBe('foo bar')
@@ -37,7 +37,7 @@ describe('classnames', () => {
* - Custom color classes
* - Arbitrary values
*/
test('tailwind-merge', () => {
it('tailwind-merge', () => {
/* eslint-disable tailwindcss/classnames-order */
expect(cn('p-0')).toBe('p-0')
expect(cn('text-right text-center text-left')).toBe('text-left')
@@ -68,7 +68,7 @@ describe('classnames', () => {
* Tests the integration of classnames and tailwind-merge:
* - Object-based conditional classes with Tailwind conflict resolution
*/
test('classnames combined with tailwind-merge', () => {
it('classnames combined with tailwind-merge', () => {
expect(cn('text-right', {
'text-center': true,
})).toBe('text-center')
@@ -83,7 +83,7 @@ describe('classnames', () => {
* - Strings, arrays, and objects in a single call
* - Tailwind merge working across different argument types
*/
test('multiple mixed argument types', () => {
it('multiple mixed argument types', () => {
expect(cn('foo', ['bar', 'baz'], { qux: true, quux: false })).toBe('foo bar baz qux')
expect(cn('p-4', ['p-2', 'm-4'], { 'text-left': true, 'text-right': true })).toBe('p-2 m-4 text-right')
})
@@ -93,7 +93,7 @@ describe('classnames', () => {
* - Deep array flattening
* - Tailwind merge with nested structures
*/
test('nested arrays', () => {
it('nested arrays', () => {
expect(cn(['foo', ['bar', 'baz']])).toBe('foo bar baz')
expect(cn(['p-4', ['p-2', 'text-center']])).toBe('p-2 text-center')
})
@@ -103,7 +103,7 @@ describe('classnames', () => {
* - Empty strings, arrays, and objects
* - Mixed empty and non-empty values
*/
test('empty inputs', () => {
it('empty inputs', () => {
expect(cn('')).toBe('')
expect(cn([])).toBe('')
expect(cn({})).toBe('')
@@ -116,7 +116,7 @@ describe('classnames', () => {
* - Truthy numbers converted to strings
* - Zero treated as falsy
*/
test('numbers as inputs', () => {
it('numbers as inputs', () => {
expect(cn(1)).toBe('1')
expect(cn(0)).toBe('')
expect(cn('foo', 1, 'bar')).toBe('foo 1 bar')
@@ -127,7 +127,7 @@ describe('classnames', () => {
* - Object merging
* - Tailwind conflict resolution across objects
*/
test('multiple objects', () => {
it('multiple objects', () => {
expect(cn({ foo: true }, { bar: true })).toBe('foo bar')
expect(cn({ foo: true, bar: false }, { bar: true, baz: true })).toBe('foo bar baz')
expect(cn({ 'p-4': true }, { 'p-2': true })).toBe('p-2')
@@ -139,7 +139,7 @@ describe('classnames', () => {
* - Nested arrays with falsy values
* - Multiple conflicting Tailwind classes
*/
test('complex edge cases', () => {
it('complex edge cases', () => {
expect(cn('foo', null, undefined, false, 'bar', 0, 1, '')).toBe('foo bar 1')
expect(cn(['foo', null, ['bar', undefined, 'baz']])).toBe('foo bar baz')
expect(cn('text-sm', { 'text-lg': false, 'text-xl': true }, 'text-2xl')).toBe('text-2xl')
@@ -150,7 +150,7 @@ describe('classnames', () => {
* - Important modifiers in objects
* - Conflict resolution with important prefix
*/
test('important modifier with objects', () => {
it('important modifier with objects', () => {
expect(cn({ '!font-medium': true }, { '!font-bold': true })).toBe('!font-bold')
expect(cn('font-normal', { '!font-bold': true })).toBe('font-normal !font-bold')
})

View File

@@ -1,4 +1,5 @@
import { type ClassValue, clsx } from 'clsx'
import type { ClassValue } from 'clsx'
import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {

View File

@@ -1,9 +1,9 @@
import { mergeValidCompletionParams } from './completion-params'
import type { FormValue, ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { mergeValidCompletionParams } from './completion-params'
describe('completion-params', () => {
describe('mergeValidCompletionParams', () => {
test('returns empty params and removedDetails for undefined oldParams', () => {
it('returns empty params and removedDetails for undefined oldParams', () => {
const rules: ModelParameterRule[] = []
const result = mergeValidCompletionParams(undefined, rules)
@@ -11,7 +11,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({})
})
test('returns empty params and removedDetails for empty oldParams', () => {
it('returns empty params and removedDetails for empty oldParams', () => {
const rules: ModelParameterRule[] = []
const result = mergeValidCompletionParams({}, rules)
@@ -19,7 +19,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({})
})
test('validates int type parameter within range', () => {
it('validates int type parameter within range', () => {
const rules: ModelParameterRule[] = [
{ name: 'max_tokens', type: 'int', min: 1, max: 4096, label: { en_US: 'Max Tokens', zh_Hans: '最大标记' }, required: false },
]
@@ -30,7 +30,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({})
})
test('removes int parameter below minimum', () => {
it('removes int parameter below minimum', () => {
const rules: ModelParameterRule[] = [
{ name: 'max_tokens', type: 'int', min: 1, max: 4096, label: { en_US: 'Max Tokens', zh_Hans: '最大标记' }, required: false },
]
@@ -41,7 +41,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({ max_tokens: 'out of range (1-4096)' })
})
test('removes int parameter above maximum', () => {
it('removes int parameter above maximum', () => {
const rules: ModelParameterRule[] = [
{ name: 'max_tokens', type: 'int', min: 1, max: 4096, label: { en_US: 'Max Tokens', zh_Hans: '最大标记' }, required: false },
]
@@ -52,7 +52,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({ max_tokens: 'out of range (1-4096)' })
})
test('removes int parameter with invalid type', () => {
it('removes int parameter with invalid type', () => {
const rules: ModelParameterRule[] = [
{ name: 'max_tokens', type: 'int', min: 1, max: 4096, label: { en_US: 'Max Tokens', zh_Hans: '最大标记' }, required: false },
]
@@ -63,7 +63,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({ max_tokens: 'invalid type' })
})
test('validates float type parameter', () => {
it('validates float type parameter', () => {
const rules: ModelParameterRule[] = [
{ name: 'temperature', type: 'float', min: 0, max: 2, label: { en_US: 'Temperature', zh_Hans: '温度' }, required: false },
]
@@ -74,7 +74,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({})
})
test('validates float at boundary values', () => {
it('validates float at boundary values', () => {
const rules: ModelParameterRule[] = [
{ name: 'temperature', type: 'float', min: 0, max: 2, label: { en_US: 'Temperature', zh_Hans: '温度' }, required: false },
]
@@ -86,7 +86,7 @@ describe('completion-params', () => {
expect(result2.params).toEqual({ temperature: 2 })
})
test('validates boolean type parameter', () => {
it('validates boolean type parameter', () => {
const rules: ModelParameterRule[] = [
{ name: 'stream', type: 'boolean', label: { en_US: 'Stream', zh_Hans: '流' }, required: false },
]
@@ -97,7 +97,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({})
})
test('removes boolean parameter with invalid type', () => {
it('removes boolean parameter with invalid type', () => {
const rules: ModelParameterRule[] = [
{ name: 'stream', type: 'boolean', label: { en_US: 'Stream', zh_Hans: '流' }, required: false },
]
@@ -108,7 +108,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({ stream: 'invalid type' })
})
test('validates string type parameter', () => {
it('validates string type parameter', () => {
const rules: ModelParameterRule[] = [
{ name: 'model', type: 'string', label: { en_US: 'Model', zh_Hans: '模型' }, required: false },
]
@@ -119,7 +119,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({})
})
test('validates string parameter with options', () => {
it('validates string parameter with options', () => {
const rules: ModelParameterRule[] = [
{ name: 'model', type: 'string', options: ['gpt-3.5-turbo', 'gpt-4'], label: { en_US: 'Model', zh_Hans: '模型' }, required: false },
]
@@ -130,7 +130,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({})
})
test('removes string parameter with invalid option', () => {
it('removes string parameter with invalid option', () => {
const rules: ModelParameterRule[] = [
{ name: 'model', type: 'string', options: ['gpt-3.5-turbo', 'gpt-4'], label: { en_US: 'Model', zh_Hans: '模型' }, required: false },
]
@@ -141,7 +141,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({ model: 'unsupported option' })
})
test('validates text type parameter', () => {
it('validates text type parameter', () => {
const rules: ModelParameterRule[] = [
{ name: 'prompt', type: 'text', label: { en_US: 'Prompt', zh_Hans: '提示' }, required: false },
]
@@ -152,7 +152,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({})
})
test('removes unsupported parameters', () => {
it('removes unsupported parameters', () => {
const rules: ModelParameterRule[] = [
{ name: 'temperature', type: 'float', min: 0, max: 2, label: { en_US: 'Temperature', zh_Hans: '温度' }, required: false },
]
@@ -163,7 +163,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({ unsupported_param: 'unsupported' })
})
test('keeps stop parameter in advanced mode even without rule', () => {
it('keeps stop parameter in advanced mode even without rule', () => {
const rules: ModelParameterRule[] = []
const oldParams: FormValue = { stop: ['END'] }
const result = mergeValidCompletionParams(oldParams, rules, true)
@@ -172,7 +172,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({})
})
test('removes stop parameter in normal mode without rule', () => {
it('removes stop parameter in normal mode without rule', () => {
const rules: ModelParameterRule[] = []
const oldParams: FormValue = { stop: ['END'] }
const result = mergeValidCompletionParams(oldParams, rules, false)
@@ -181,7 +181,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({ stop: 'unsupported' })
})
test('handles multiple parameters with mixed validity', () => {
it('handles multiple parameters with mixed validity', () => {
const rules: ModelParameterRule[] = [
{ name: 'temperature', type: 'float', min: 0, max: 2, label: { en_US: 'Temperature', zh_Hans: '温度' }, required: false },
{ name: 'max_tokens', type: 'int', min: 1, max: 4096, label: { en_US: 'Max Tokens', zh_Hans: '最大标记' }, required: false },
@@ -205,7 +205,7 @@ describe('completion-params', () => {
})
})
test('handles parameters without min/max constraints', () => {
it('handles parameters without min/max constraints', () => {
const rules: ModelParameterRule[] = [
{ name: 'value', type: 'int', label: { en_US: 'Value', zh_Hans: '值' }, required: false },
]
@@ -216,7 +216,7 @@ describe('completion-params', () => {
expect(result.removedDetails).toEqual({})
})
test('removes parameter with unsupported rule type', () => {
it('removes parameter with unsupported rule type', () => {
const rules: ModelParameterRule[] = [
{ name: 'custom', type: 'unknown_type', label: { en_US: 'Custom', zh_Hans: '自定义' }, required: false } as any,
]

View File

@@ -4,7 +4,7 @@ export const mergeValidCompletionParams = (
oldParams: FormValue | undefined,
rules: ModelParameterRule[],
isAdvancedMode: boolean = false,
): { params: FormValue; removedDetails: Record<string, string> } => {
): { params: FormValue, removedDetails: Record<string, string> } => {
if (!oldParams || Object.keys(oldParams).length === 0)
return { params: {}, removedDetails: {} }
@@ -81,7 +81,7 @@ export const fetchAndMergeValidCompletionParams = async (
modelId: string,
oldParams: FormValue | undefined,
isAdvancedMode: boolean = false,
): Promise<{ params: FormValue; removedDetails: Record<string, string> }> => {
): Promise<{ params: FormValue, removedDetails: Record<string, string> }> => {
const { fetchModelParameterRules } = await import('@/service/common')
const url = `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}`
const { data: parameterRules } = await fetchModelParameterRules(url)

View File

@@ -1,3 +1,4 @@
import { renderHook } from '@testing-library/react'
/**
* Test suite for React context creation utilities
*
@@ -9,7 +10,6 @@
* - createSelectorCtx: Context with selector support using use-context-selector library
*/
import React from 'react'
import { renderHook } from '@testing-library/react'
import { createCtx, createSelectorCtx } from './context'
describe('Context Utilities', () => {
@@ -106,8 +106,8 @@ describe('Context Utilities', () => {
*/
it('should handle complex context values', () => {
type ComplexContext = {
user: { id: string; name: string }
settings: { theme: string; locale: string }
user: { id: string, name: string }
settings: { theme: string, locale: string }
actions: Array<() => void>
}

View File

@@ -1,9 +1,11 @@
import { type Context, type Provider, createContext, useContext } from 'react'
import type { Context, Provider } from 'react'
import { createContext, useContext } from 'react'
import * as selector from 'use-context-selector'
const createCreateCtxFunction = (
useContextImpl: typeof useContext,
createContextImpl: typeof createContext) => {
createContextImpl: typeof createContext,
) => {
return function<T>({ name, defaultValue }: CreateCtxOptions<T> = {}): CreateCtxReturn<T> {
const emptySymbol = Symbol(`empty ${name}`)
// @ts-expect-error it's ok here

View File

@@ -1,6 +1,6 @@
import type { Mock } from 'vitest'
import { searchEmoji } from './emoji'
import { SearchIndex } from 'emoji-mart'
import { searchEmoji } from './emoji'
vi.mock('emoji-mart', () => ({
SearchIndex: {

View File

@@ -1,5 +1,5 @@
import { SearchIndex } from 'emoji-mart'
import type { Emoji } from '@emoji-mart/data'
import { SearchIndex } from 'emoji-mart'
export async function searchEmoji(value: string) {
const emojis: Emoji[] = await SearchIndex.search(value) || []

View File

@@ -14,15 +14,15 @@
*/
export function encryptField(plaintext: string): string {
try {
// Base64 encode the plaintext
// btoa works with ASCII, so we need to handle UTF-8 properly
// Base64 encode the plaintext
// btoa works with ASCII, so we need to handle UTF-8 properly
const utf8Bytes = new TextEncoder().encode(plaintext)
const base64 = btoa(String.fromCharCode(...utf8Bytes))
return base64
}
catch (error) {
console.error('Field encoding failed:', error)
// If encoding fails, throw error to prevent sending plaintext
// If encoding fails, throw error to prevent sending plaintext
throw new Error('Encoding failed. Please check your input.')
}
}

View File

@@ -1,67 +1,67 @@
import { downloadFile, formatFileSize, formatNumber, formatNumberAbbreviated, formatTime } from './format'
describe('formatNumber', () => {
test('should correctly format integers', () => {
it('should correctly format integers', () => {
expect(formatNumber(1234567)).toBe('1,234,567')
})
test('should correctly format decimals', () => {
it('should correctly format decimals', () => {
expect(formatNumber(1234567.89)).toBe('1,234,567.89')
})
test('should correctly handle string input', () => {
it('should correctly handle string input', () => {
expect(formatNumber('1234567')).toBe('1,234,567')
})
test('should correctly handle zero', () => {
it('should correctly handle zero', () => {
expect(formatNumber(0)).toBe(0)
})
test('should correctly handle negative numbers', () => {
it('should correctly handle negative numbers', () => {
expect(formatNumber(-1234567)).toBe('-1,234,567')
})
test('should correctly handle empty input', () => {
it('should correctly handle empty input', () => {
expect(formatNumber('')).toBe('')
})
})
describe('formatFileSize', () => {
test('should return the input if it is falsy', () => {
it('should return the input if it is falsy', () => {
expect(formatFileSize(0)).toBe(0)
})
test('should format bytes correctly', () => {
it('should format bytes correctly', () => {
expect(formatFileSize(500)).toBe('500.00 bytes')
})
test('should format kilobytes correctly', () => {
it('should format kilobytes correctly', () => {
expect(formatFileSize(1500)).toBe('1.46 KB')
})
test('should format megabytes correctly', () => {
it('should format megabytes correctly', () => {
expect(formatFileSize(1500000)).toBe('1.43 MB')
})
test('should format gigabytes correctly', () => {
it('should format gigabytes correctly', () => {
expect(formatFileSize(1500000000)).toBe('1.40 GB')
})
test('should format terabytes correctly', () => {
it('should format terabytes correctly', () => {
expect(formatFileSize(1500000000000)).toBe('1.36 TB')
})
test('should format petabytes correctly', () => {
it('should format petabytes correctly', () => {
expect(formatFileSize(1500000000000000)).toBe('1.33 PB')
})
})
describe('formatTime', () => {
test('should return the input if it is falsy', () => {
it('should return the input if it is falsy', () => {
expect(formatTime(0)).toBe(0)
})
test('should format seconds correctly', () => {
it('should format seconds correctly', () => {
expect(formatTime(30)).toBe('30.00 sec')
})
test('should format minutes correctly', () => {
it('should format minutes correctly', () => {
expect(formatTime(90)).toBe('1.50 min')
})
test('should format hours correctly', () => {
it('should format hours correctly', () => {
expect(formatTime(3600)).toBe('1.00 h')
})
test('should handle large numbers', () => {
it('should handle large numbers', () => {
expect(formatTime(7200)).toBe('2.00 h')
})
})
describe('downloadFile', () => {
test('should create a link and trigger a download correctly', () => {
it('should create a link and trigger a download correctly', () => {
// Mock data
const blob = new Blob(['test content'], { type: 'text/plain' })
const fileName = 'test-file.txt'

View File

@@ -1,5 +1,5 @@
import type { Locale } from '@/i18n-config'
import type { Dayjs } from 'dayjs'
import type { Locale } from '@/i18n-config'
import 'dayjs/locale/de'
import 'dayjs/locale/es'
import 'dayjs/locale/fa'
@@ -95,7 +95,7 @@ export const formatTime = (seconds: number) => {
return `${seconds.toFixed(2)} ${units[index]}`
}
export const downloadFile = ({ data, fileName }: { data: Blob; fileName: string }) => {
export const downloadFile = ({ data, fileName }: { data: Blob, fileName: string }) => {
const url = window.URL.createObjectURL(data)
const a = document.createElement('a')
a.href = url
@@ -119,7 +119,8 @@ export const downloadFile = ({ data, fileName }: { data: Blob; fileName: string
*/
export const formatNumberAbbreviated = (num: number) => {
// If less than 1000, return as-is
if (num < 1000) return num.toString()
if (num < 1000)
return num.toString()
// Define thresholds and suffixes
const units = [

View File

@@ -1,16 +1,16 @@
import { MARKETPLACE_API_PREFIX } from '@/config'
/**
* Test suite for icon utility functions
* Tests the generation of marketplace plugin icon URLs
*/
import { getIconFromMarketPlace } from './get-icon'
import { MARKETPLACE_API_PREFIX } from '@/config'
describe('get-icon', () => {
describe('getIconFromMarketPlace', () => {
/**
* Tests basic URL generation for marketplace plugin icons
*/
test('returns correct marketplace icon URL', () => {
it('returns correct marketplace icon URL', () => {
const pluginId = 'test-plugin-123'
const result = getIconFromMarketPlace(pluginId)
expect(result).toBe(`${MARKETPLACE_API_PREFIX}/plugins/${pluginId}/icon`)
@@ -20,7 +20,7 @@ describe('get-icon', () => {
* Tests URL generation with plugin IDs containing special characters
* like dashes and underscores
*/
test('handles plugin ID with special characters', () => {
it('handles plugin ID with special characters', () => {
const pluginId = 'plugin-with-dashes_and_underscores'
const result = getIconFromMarketPlace(pluginId)
expect(result).toBe(`${MARKETPLACE_API_PREFIX}/plugins/${pluginId}/icon`)
@@ -30,7 +30,7 @@ describe('get-icon', () => {
* Tests behavior with empty plugin ID
* Note: This creates a malformed URL but doesn't throw an error
*/
test('handles empty plugin ID', () => {
it('handles empty plugin ID', () => {
const pluginId = ''
const result = getIconFromMarketPlace(pluginId)
expect(result).toBe(`${MARKETPLACE_API_PREFIX}/plugins//icon`)
@@ -40,7 +40,7 @@ describe('get-icon', () => {
* Tests URL generation with plugin IDs containing spaces
* Spaces will be URL-encoded when actually used
*/
test('handles plugin ID with spaces', () => {
it('handles plugin ID with spaces', () => {
const pluginId = 'plugin with spaces'
const result = getIconFromMarketPlace(pluginId)
expect(result).toBe(`${MARKETPLACE_API_PREFIX}/plugins/${pluginId}/icon`)
@@ -51,7 +51,7 @@ describe('get-icon', () => {
* These tests document current behavior and potential security concerns
* Note: Current implementation does not sanitize path traversal sequences
*/
test('handles path traversal attempts', () => {
it('handles path traversal attempts', () => {
const pluginId = '../../../etc/passwd'
const result = getIconFromMarketPlace(pluginId)
// Current implementation includes path traversal sequences in URL
@@ -60,7 +60,7 @@ describe('get-icon', () => {
expect(result).toContain(pluginId)
})
test('handles multiple path traversal attempts', () => {
it('handles multiple path traversal attempts', () => {
const pluginId = '../../../../etc/passwd'
const result = getIconFromMarketPlace(pluginId)
// Current implementation includes path traversal sequences in URL
@@ -68,7 +68,7 @@ describe('get-icon', () => {
expect(result).toContain(pluginId)
})
test('passes through URL-encoded path traversal sequences', () => {
it('passes through URL-encoded path traversal sequences', () => {
const pluginId = '..%2F..%2Fetc%2Fpasswd'
const result = getIconFromMarketPlace(pluginId)
expect(result).toContain(pluginId)
@@ -79,14 +79,14 @@ describe('get-icon', () => {
* These tests document current behavior with invalid input types
* Note: Current implementation converts null/undefined to strings instead of throwing
*/
test('handles null plugin ID', () => {
it('handles null plugin ID', () => {
// Current implementation converts null to string "null"
const result = getIconFromMarketPlace(null as any)
expect(result).toContain('null')
// This is a potential issue - should validate input type
})
test('handles undefined plugin ID', () => {
it('handles undefined plugin ID', () => {
// Current implementation converts undefined to string "undefined"
const result = getIconFromMarketPlace(undefined as any)
expect(result).toContain('undefined')
@@ -97,7 +97,7 @@ describe('get-icon', () => {
* Security tests: URL-sensitive characters
* These tests verify that URL-sensitive characters are handled appropriately
*/
test('does not encode URL-sensitive characters', () => {
it('does not encode URL-sensitive characters', () => {
const pluginId = 'plugin/with?special=chars#hash'
const result = getIconFromMarketPlace(pluginId)
// Note: Current implementation doesn't encode, but test documents the behavior
@@ -107,7 +107,7 @@ describe('get-icon', () => {
expect(result).toContain('=')
})
test('handles URL characters like & and %', () => {
it('handles URL characters like & and %', () => {
const pluginId = 'plugin&with%encoding'
const result = getIconFromMarketPlace(pluginId)
expect(result).toContain(pluginId)
@@ -117,20 +117,20 @@ describe('get-icon', () => {
* Edge case tests: Extreme inputs
* These tests verify behavior with unusual but valid inputs
*/
test('handles very long plugin ID', () => {
it('handles very long plugin ID', () => {
const pluginId = 'a'.repeat(10000)
const result = getIconFromMarketPlace(pluginId)
expect(result).toContain(pluginId)
expect(result.length).toBeGreaterThan(10000)
})
test('handles Unicode characters', () => {
it('handles Unicode characters', () => {
const pluginId = '插件-🚀-测试-日本語'
const result = getIconFromMarketPlace(pluginId)
expect(result).toContain(pluginId)
})
test('handles control characters', () => {
it('handles control characters', () => {
const pluginId = 'plugin\nwith\ttabs\r\nand\0null'
const result = getIconFromMarketPlace(pluginId)
expect(result).toContain(pluginId)
@@ -140,20 +140,20 @@ describe('get-icon', () => {
* Security tests: XSS attempts
* These tests verify that XSS attempts are handled appropriately
*/
test('handles XSS attempts with script tags', () => {
it('handles XSS attempts with script tags', () => {
const pluginId = '<script>alert("xss")</script>'
const result = getIconFromMarketPlace(pluginId)
expect(result).toContain(pluginId)
// Note: Current implementation doesn't sanitize, but test documents the behavior
})
test('handles XSS attempts with event handlers', () => {
it('handles XSS attempts with event handlers', () => {
const pluginId = 'plugin"onerror="alert(1)"'
const result = getIconFromMarketPlace(pluginId)
expect(result).toContain(pluginId)
})
test('handles XSS attempts with encoded script tags', () => {
it('handles XSS attempts with encoded script tags', () => {
const pluginId = '%3Cscript%3Ealert%28%22xss%22%29%3C%2Fscript%3E'
const result = getIconFromMarketPlace(pluginId)
expect(result).toContain(pluginId)

View File

@@ -408,7 +408,7 @@ describe('randomString extended', () => {
})
it('should only contain valid characters', () => {
const validChars = /^[0-9a-zA-Z_-]+$/
const validChars = /^[\w-]+$/
const str = randomString(100)
expect(validChars.test(str)).toBe(true)
})

View File

@@ -13,39 +13,39 @@ describe('mcp', () => {
/**
* The link emoji (🔗) is used as a special marker for MCP icons
*/
test('returns true for emoji object with 🔗 content', () => {
it('returns true for emoji object with 🔗 content', () => {
const src = { content: '🔗', background: '#fff' }
expect(shouldUseMcpIcon(src)).toBe(true)
})
test('returns false for emoji object with different content', () => {
it('returns false for emoji object with different content', () => {
const src = { content: '🎉', background: '#fff' }
expect(shouldUseMcpIcon(src)).toBe(false)
})
test('returns false for string URL', () => {
it('returns false for string URL', () => {
const src = 'https://example.com/icon.png'
expect(shouldUseMcpIcon(src)).toBe(false)
})
test('returns false for null', () => {
it('returns false for null', () => {
expect(shouldUseMcpIcon(null)).toBe(false)
})
test('returns false for undefined', () => {
it('returns false for undefined', () => {
expect(shouldUseMcpIcon(undefined)).toBe(false)
})
test('returns false for empty object', () => {
it('returns false for empty object', () => {
expect(shouldUseMcpIcon({})).toBe(false)
})
test('returns false for object without content property', () => {
it('returns false for object without content property', () => {
const src = { background: '#fff' }
expect(shouldUseMcpIcon(src)).toBe(false)
})
test('returns false for object with null content', () => {
it('returns false for object with null content', () => {
const src = { content: null, background: '#fff' }
expect(shouldUseMcpIcon(src)).toBe(false)
})
@@ -61,27 +61,27 @@ describe('mcp', () => {
* - Icon type is 'emoji'
* - Icon content is the link emoji (🔗)
*/
test('returns true when iconType is emoji and icon is 🔗', () => {
it('returns true when iconType is emoji and icon is 🔗', () => {
expect(shouldUseMcpIconForAppIcon('emoji', '🔗')).toBe(true)
})
test('returns false when iconType is emoji but icon is different', () => {
it('returns false when iconType is emoji but icon is different', () => {
expect(shouldUseMcpIconForAppIcon('emoji', '🎉')).toBe(false)
})
test('returns false when iconType is image', () => {
it('returns false when iconType is image', () => {
expect(shouldUseMcpIconForAppIcon('image', '🔗')).toBe(false)
})
test('returns false when iconType is image and icon is different', () => {
it('returns false when iconType is image and icon is different', () => {
expect(shouldUseMcpIconForAppIcon('image', 'file-id-123')).toBe(false)
})
test('returns false for empty strings', () => {
it('returns false for empty strings', () => {
expect(shouldUseMcpIconForAppIcon('', '')).toBe(false)
})
test('returns false when iconType is empty but icon is 🔗', () => {
it('returns false when iconType is empty but icon is 🔗', () => {
expect(shouldUseMcpIconForAppIcon('', '🔗')).toBe(false)
})
})

View File

@@ -1,3 +1,5 @@
import type { PromptVariable } from '@/models/debug'
import type { UserInputFormItem } from '@/types/app'
/**
* Test suite for model configuration transformation utilities
*
@@ -15,8 +17,6 @@ import {
promptVariablesToUserInputsForm,
userInputsFormToPromptVariables,
} from './model-config'
import type { UserInputFormItem } from '@/types/app'
import type { PromptVariable } from '@/models/debug'
describe('Model Config Utilities', () => {
describe('userInputsFormToPromptVariables', () => {

View File

@@ -1,5 +1,5 @@
import type { UserInputFormItem } from '@/types/app'
import type { PromptVariable } from '@/models/debug'
import type { UserInputFormItem } from '@/types/app'
export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | null, dataset_query_variable?: string) => {
if (!useInputs)
@@ -196,7 +196,7 @@ export const promptVariablesToUserInputsForm = (promptVariables: PromptVariable[
}
export const formatBooleanInputs = (useInputs?: PromptVariable[] | null, inputs?: Record<string, string | number | object | boolean> | null) => {
if(!useInputs)
if (!useInputs)
return inputs
const res = { ...inputs }
useInputs.forEach((item) => {

View File

@@ -33,28 +33,28 @@ describe('navigation', () => {
* Tests createNavigationPath which builds URLs with optional query parameter preservation
*/
describe('createNavigationPath', () => {
test('preserves query parameters by default', () => {
it('preserves query parameters by default', () => {
const result = createNavigationPath('/datasets/123/documents')
expect(result).toBe('/datasets/123/documents?page=3&limit=10&keyword=test')
})
test('returns clean path when preserveParams is false', () => {
it('returns clean path when preserveParams is false', () => {
const result = createNavigationPath('/datasets/123/documents', false)
expect(result).toBe('/datasets/123/documents')
})
test('handles empty query string', () => {
it('handles empty query string', () => {
globalThis.window.location.search = ''
const result = createNavigationPath('/datasets/123/documents')
expect(result).toBe('/datasets/123/documents')
})
test('handles path with trailing slash', () => {
it('handles path with trailing slash', () => {
const result = createNavigationPath('/datasets/123/documents/')
expect(result).toBe('/datasets/123/documents/?page=3&limit=10&keyword=test')
})
test('handles root path', () => {
it('handles root path', () => {
const result = createNavigationPath('/')
expect(result).toBe('/?page=3&limit=10&keyword=test')
})
@@ -67,7 +67,7 @@ describe('navigation', () => {
/**
* Tests that the returned function properly navigates with preserved params
*/
test('returns function that calls router.push with correct path', () => {
it('returns function that calls router.push with correct path', () => {
const mockRouter = { push: vi.fn() }
const backNav = createBackNavigation(mockRouter, '/datasets/123/documents')
@@ -76,7 +76,7 @@ describe('navigation', () => {
expect(mockRouter.push).toHaveBeenCalledWith('/datasets/123/documents?page=3&limit=10&keyword=test')
})
test('returns function that navigates without params when preserveParams is false', () => {
it('returns function that navigates without params when preserveParams is false', () => {
const mockRouter = { push: vi.fn() }
const backNav = createBackNavigation(mockRouter, '/datasets/123/documents', false)
@@ -85,7 +85,7 @@ describe('navigation', () => {
expect(mockRouter.push).toHaveBeenCalledWith('/datasets/123/documents')
})
test('can be called multiple times', () => {
it('can be called multiple times', () => {
const mockRouter = { push: vi.fn() }
const backNav = createBackNavigation(mockRouter, '/datasets/123/documents')
@@ -103,32 +103,32 @@ describe('navigation', () => {
/**
* Tests selective parameter extraction
*/
test('extracts specified parameters', () => {
it('extracts specified parameters', () => {
const result = extractQueryParams(['page', 'limit'])
expect(result).toEqual({ page: '3', limit: '10' })
})
test('extracts all specified parameters including keyword', () => {
it('extracts all specified parameters including keyword', () => {
const result = extractQueryParams(['page', 'limit', 'keyword'])
expect(result).toEqual({ page: '3', limit: '10', keyword: 'test' })
})
test('ignores non-existent parameters', () => {
it('ignores non-existent parameters', () => {
const result = extractQueryParams(['page', 'nonexistent'])
expect(result).toEqual({ page: '3' })
})
test('returns empty object when no parameters match', () => {
it('returns empty object when no parameters match', () => {
const result = extractQueryParams(['foo', 'bar'])
expect(result).toEqual({})
})
test('returns empty object for empty array', () => {
it('returns empty object for empty array', () => {
const result = extractQueryParams([])
expect(result).toEqual({})
})
test('handles empty query string', () => {
it('handles empty query string', () => {
globalThis.window.location.search = ''
const result = extractQueryParams(['page', 'limit'])
expect(result).toEqual({})
@@ -142,7 +142,7 @@ describe('navigation', () => {
/**
* Tests URL construction with custom parameters
*/
test('creates path with specified parameters', () => {
it('creates path with specified parameters', () => {
const result = createNavigationPathWithParams('/datasets/123/documents', {
page: '1',
limit: '25',
@@ -150,7 +150,7 @@ describe('navigation', () => {
expect(result).toBe('/datasets/123/documents?page=1&limit=25')
})
test('handles string and number values', () => {
it('handles string and number values', () => {
const result = createNavigationPathWithParams('/datasets/123/documents', {
page: 1,
limit: 25,
@@ -159,7 +159,7 @@ describe('navigation', () => {
expect(result).toBe('/datasets/123/documents?page=1&limit=25&keyword=search')
})
test('filters out empty string values', () => {
it('filters out empty string values', () => {
const result = createNavigationPathWithParams('/datasets/123/documents', {
page: '1',
keyword: '',
@@ -167,7 +167,7 @@ describe('navigation', () => {
expect(result).toBe('/datasets/123/documents?page=1')
})
test('filters out null and undefined values', () => {
it('filters out null and undefined values', () => {
const result = createNavigationPathWithParams('/datasets/123/documents', {
page: '1',
keyword: null as any,
@@ -176,12 +176,12 @@ describe('navigation', () => {
expect(result).toBe('/datasets/123/documents?page=1')
})
test('returns base path when params are empty', () => {
it('returns base path when params are empty', () => {
const result = createNavigationPathWithParams('/datasets/123/documents', {})
expect(result).toBe('/datasets/123/documents')
})
test('encodes special characters in values', () => {
it('encodes special characters in values', () => {
const result = createNavigationPathWithParams('/datasets/123/documents', {
keyword: 'search term',
})
@@ -196,51 +196,51 @@ describe('navigation', () => {
/**
* Tests parameter merging and overriding
*/
test('merges new params with existing ones', () => {
it('merges new params with existing ones', () => {
const result = mergeQueryParams({ keyword: 'new', page: '1' })
expect(result.get('page')).toBe('1')
expect(result.get('limit')).toBe('10')
expect(result.get('keyword')).toBe('new')
})
test('overrides existing parameters', () => {
it('overrides existing parameters', () => {
const result = mergeQueryParams({ page: '5' })
expect(result.get('page')).toBe('5')
expect(result.get('limit')).toBe('10')
})
test('adds new parameters', () => {
it('adds new parameters', () => {
const result = mergeQueryParams({ filter: 'active' })
expect(result.get('filter')).toBe('active')
expect(result.get('page')).toBe('3')
})
test('removes parameters with null value', () => {
it('removes parameters with null value', () => {
const result = mergeQueryParams({ page: null })
expect(result.get('page')).toBeNull()
expect(result.get('limit')).toBe('10')
})
test('removes parameters with undefined value', () => {
it('removes parameters with undefined value', () => {
const result = mergeQueryParams({ page: undefined })
expect(result.get('page')).toBeNull()
expect(result.get('limit')).toBe('10')
})
test('does not preserve existing when preserveExisting is false', () => {
it('does not preserve existing when preserveExisting is false', () => {
const result = mergeQueryParams({ filter: 'active' }, false)
expect(result.get('filter')).toBe('active')
expect(result.get('page')).toBeNull()
expect(result.get('limit')).toBeNull()
})
test('handles number values', () => {
it('handles number values', () => {
const result = mergeQueryParams({ page: 5, limit: 20 })
expect(result.get('page')).toBe('5')
expect(result.get('limit')).toBe('20')
})
test('does not add empty string values', () => {
it('does not add empty string values', () => {
const result = mergeQueryParams({ newParam: '' })
expect(result.get('newParam')).toBeNull()
// Existing params are preserved
@@ -256,7 +256,7 @@ describe('navigation', () => {
* Tests navigation back to dataset documents list
*/
describe('backToDocuments', () => {
test('creates navigation function with preserved params', () => {
it('creates navigation function with preserved params', () => {
const mockRouter = { push: vi.fn() }
const backNav = datasetNavigation.backToDocuments(mockRouter, 'dataset-123')
@@ -270,7 +270,7 @@ describe('navigation', () => {
* Tests navigation to document detail page
*/
describe('toDocumentDetail', () => {
test('creates navigation function to document detail', () => {
it('creates navigation function to document detail', () => {
const mockRouter = { push: vi.fn() }
const navFunc = datasetNavigation.toDocumentDetail(mockRouter, 'dataset-123', 'doc-456')
@@ -284,7 +284,7 @@ describe('navigation', () => {
* Tests navigation to document settings page
*/
describe('toDocumentSettings', () => {
test('creates navigation function to document settings', () => {
it('creates navigation function to document settings', () => {
const mockRouter = { push: vi.fn() }
const navFunc = datasetNavigation.toDocumentSettings(mockRouter, 'dataset-123', 'doc-456')

View File

@@ -1,9 +1,9 @@
import { DatasetPermission } from '@/models/datasets'
/**
* Test suite for permission utility functions
* Tests dataset edit permission logic based on user roles and dataset settings
*/
import { hasEditPermissionForDataset } from './permission'
import { DatasetPermission } from '@/models/datasets'
describe('permission', () => {
/**
@@ -18,7 +18,7 @@ describe('permission', () => {
const creatorId = 'creator-456'
const otherUserId = 'user-789'
test('returns true when permission is onlyMe and user is creator', () => {
it('returns true when permission is onlyMe and user is creator', () => {
const config = {
createdBy: userId,
partialMemberList: [],
@@ -27,7 +27,7 @@ describe('permission', () => {
expect(hasEditPermissionForDataset(userId, config)).toBe(true)
})
test('returns false when permission is onlyMe and user is not creator', () => {
it('returns false when permission is onlyMe and user is not creator', () => {
const config = {
createdBy: creatorId,
partialMemberList: [],
@@ -36,7 +36,7 @@ describe('permission', () => {
expect(hasEditPermissionForDataset(userId, config)).toBe(false)
})
test('returns true when permission is allTeamMembers for any user', () => {
it('returns true when permission is allTeamMembers for any user', () => {
const config = {
createdBy: creatorId,
partialMemberList: [],
@@ -47,7 +47,7 @@ describe('permission', () => {
expect(hasEditPermissionForDataset(creatorId, config)).toBe(true)
})
test('returns true when permission is partialMembers and user is in list', () => {
it('returns true when permission is partialMembers and user is in list', () => {
const config = {
createdBy: creatorId,
partialMemberList: [userId, otherUserId],
@@ -56,7 +56,7 @@ describe('permission', () => {
expect(hasEditPermissionForDataset(userId, config)).toBe(true)
})
test('returns false when permission is partialMembers and user is not in list', () => {
it('returns false when permission is partialMembers and user is not in list', () => {
const config = {
createdBy: creatorId,
partialMemberList: [otherUserId],
@@ -65,7 +65,7 @@ describe('permission', () => {
expect(hasEditPermissionForDataset(userId, config)).toBe(false)
})
test('returns false when permission is partialMembers with empty list', () => {
it('returns false when permission is partialMembers with empty list', () => {
const config = {
createdBy: creatorId,
partialMemberList: [],
@@ -74,7 +74,7 @@ describe('permission', () => {
expect(hasEditPermissionForDataset(userId, config)).toBe(false)
})
test('creator is not automatically granted access with partialMembers permission', () => {
it('creator is not automatically granted access with partialMembers permission', () => {
const config = {
createdBy: creatorId,
partialMemberList: [userId],
@@ -83,7 +83,7 @@ describe('permission', () => {
expect(hasEditPermissionForDataset(creatorId, config)).toBe(false)
})
test('creator has access when included in partialMemberList', () => {
it('creator has access when included in partialMemberList', () => {
const config = {
createdBy: creatorId,
partialMemberList: [creatorId, userId],

View File

@@ -10,36 +10,36 @@ describe('time', () => {
* Returns true if the first date is after the second
*/
describe('isAfter', () => {
test('returns true when first date is after second date', () => {
it('returns true when first date is after second date', () => {
const date1 = '2024-01-02'
const date2 = '2024-01-01'
expect(isAfter(date1, date2)).toBe(true)
})
test('returns false when first date is before second date', () => {
it('returns false when first date is before second date', () => {
const date1 = '2024-01-01'
const date2 = '2024-01-02'
expect(isAfter(date1, date2)).toBe(false)
})
test('returns false when dates are equal', () => {
it('returns false when dates are equal', () => {
const date = '2024-01-01'
expect(isAfter(date, date)).toBe(false)
})
test('works with Date objects', () => {
it('works with Date objects', () => {
const date1 = new Date('2024-01-02')
const date2 = new Date('2024-01-01')
expect(isAfter(date1, date2)).toBe(true)
})
test('works with timestamps', () => {
it('works with timestamps', () => {
const date1 = 1704240000000 // 2024-01-03
const date2 = 1704153600000 // 2024-01-02
expect(isAfter(date1, date2)).toBe(true)
})
test('handles time differences within same day', () => {
it('handles time differences within same day', () => {
const date1 = '2024-01-01 12:00:00'
const date2 = '2024-01-01 11:00:00'
expect(isAfter(date1, date2)).toBe(true)
@@ -54,44 +54,44 @@ describe('time', () => {
/**
* Tests basic date formatting with standard format
*/
test('formats date with YYYY-MM-DD format', () => {
it('formats date with YYYY-MM-DD format', () => {
const date = '2024-01-15'
const result = formatTime({ date, dateFormat: 'YYYY-MM-DD' })
expect(result).toBe('2024-01-15')
})
test('formats date with custom format', () => {
it('formats date with custom format', () => {
const date = '2024-01-15 14:30:00'
const result = formatTime({ date, dateFormat: 'MMM DD, YYYY HH:mm' })
expect(result).toBe('Jan 15, 2024 14:30')
})
test('formats date with full month name', () => {
it('formats date with full month name', () => {
const date = '2024-01-15'
const result = formatTime({ date, dateFormat: 'MMMM DD, YYYY' })
expect(result).toBe('January 15, 2024')
})
test('formats date with time only', () => {
it('formats date with time only', () => {
const date = '2024-01-15 14:30:45'
const result = formatTime({ date, dateFormat: 'HH:mm:ss' })
expect(result).toBe('14:30:45')
})
test('works with Date objects', () => {
it('works with Date objects', () => {
const date = new Date(2024, 0, 15) // Month is 0-indexed
const result = formatTime({ date, dateFormat: 'YYYY-MM-DD' })
expect(result).toBe('2024-01-15')
})
test('works with timestamps', () => {
it('works with timestamps', () => {
const date = 1705276800000 // 2024-01-15 00:00:00 UTC
const result = formatTime({ date, dateFormat: 'YYYY-MM-DD' })
// Account for timezone differences: UTC-5 to UTC+8 can result in 2024-01-14 or 2024-01-15
expect(result).toMatch(/^2024-01-(14|15)$/)
})
test('handles ISO 8601 format', () => {
it('handles ISO 8601 format', () => {
const date = '2024-01-15T14:30:00Z'
const result = formatTime({ date, dateFormat: 'YYYY-MM-DD HH:mm' })
expect(result).toContain('2024-01-15')

View File

@@ -1,4 +1,5 @@
import dayjs, { type ConfigType } from 'dayjs'
import type { ConfigType } from 'dayjs'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
dayjs.extend(utc)
@@ -7,7 +8,7 @@ export const isAfter = (date: ConfigType, compare: ConfigType) => {
return dayjs(date).isAfter(dayjs(compare))
}
export const formatTime = ({ date, dateFormat }: { date: ConfigType; dateFormat: string }) => {
export const formatTime = ({ date, dateFormat }: { date: ConfigType, dateFormat: string }) => {
return dayjs(date).format(dateFormat)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
/**
* Test suite for tool call utility functions
* Tests detection of function/tool call support in AI models
*/
import { supportFunctionCall } from './tool-call'
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
describe('tool-call', () => {
/**
@@ -14,7 +14,7 @@ describe('tool-call', () => {
/**
* Tests detection of basic tool call support
*/
test('returns true when features include toolCall', () => {
it('returns true when features include toolCall', () => {
const features = [ModelFeatureEnum.toolCall]
expect(supportFunctionCall(features)).toBe(true)
})
@@ -22,7 +22,7 @@ describe('tool-call', () => {
/**
* Tests detection of multi-tool call support (calling multiple tools in one request)
*/
test('returns true when features include multiToolCall', () => {
it('returns true when features include multiToolCall', () => {
const features = [ModelFeatureEnum.multiToolCall]
expect(supportFunctionCall(features)).toBe(true)
})
@@ -30,12 +30,12 @@ describe('tool-call', () => {
/**
* Tests detection of streaming tool call support
*/
test('returns true when features include streamToolCall', () => {
it('returns true when features include streamToolCall', () => {
const features = [ModelFeatureEnum.streamToolCall]
expect(supportFunctionCall(features)).toBe(true)
})
test('returns true when features include multiple tool call types', () => {
it('returns true when features include multiple tool call types', () => {
const features = [
ModelFeatureEnum.toolCall,
ModelFeatureEnum.multiToolCall,
@@ -47,7 +47,7 @@ describe('tool-call', () => {
/**
* Tests that tool call support is detected even when mixed with other features
*/
test('returns true when features include tool call among other features', () => {
it('returns true when features include tool call among other features', () => {
const features = [
ModelFeatureEnum.agentThought,
ModelFeatureEnum.toolCall,
@@ -59,20 +59,20 @@ describe('tool-call', () => {
/**
* Tests that false is returned when no tool call features are present
*/
test('returns false when features do not include any tool call type', () => {
it('returns false when features do not include any tool call type', () => {
const features = [ModelFeatureEnum.agentThought, ModelFeatureEnum.vision]
expect(supportFunctionCall(features)).toBe(false)
})
test('returns false for empty array', () => {
it('returns false for empty array', () => {
expect(supportFunctionCall([])).toBe(false)
})
test('returns false for undefined', () => {
it('returns false for undefined', () => {
expect(supportFunctionCall(undefined)).toBe(false)
})
test('returns false for null', () => {
it('returns false for null', () => {
expect(supportFunctionCall(null as any)).toBe(false)
})
})

View File

@@ -1,6 +1,7 @@
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
export const supportFunctionCall = (features: ModelFeatureEnum[] = []): boolean => {
if (!features || !features.length) return false
if (!features || !features.length)
return false
return features.some(feature => [ModelFeatureEnum.toolCall, ModelFeatureEnum.multiToolCall, ModelFeatureEnum.streamToolCall].includes(feature))
}

View File

@@ -15,8 +15,9 @@ export function validateRedirectUrl(url: string): void {
if (
error instanceof Error
&& error.message === 'Authorization URL must be HTTP or HTTPS'
)
) {
throw error
}
// If URL parsing fails, it's also invalid
throw new Error(`Invalid URL: ${url}`)
}

View File

@@ -1,3 +1,4 @@
import { InputVarType } from '@/app/components/workflow/types'
import {
checkKey,
checkKeys,
@@ -8,7 +9,6 @@ import {
hasDuplicateStr,
replaceSpaceWithUnderscoreInVarNameInput,
} from './var'
import { InputVarType } from '@/app/components/workflow/types'
describe('Variable Utilities', () => {
describe('checkKey', () => {

View File

@@ -1,12 +1,12 @@
import { MARKETPLACE_URL_PREFIX, MAX_VAR_KEY_LENGTH, VAR_ITEM_TEMPLATE, VAR_ITEM_TEMPLATE_IN_WORKFLOW, getMaxVarNameLength } from '@/config'
import type { InputVar } from '@/app/components/workflow/types'
import {
CONTEXT_PLACEHOLDER_TEXT,
HISTORY_PLACEHOLDER_TEXT,
PRE_PROMPT_PLACEHOLDER_TEXT,
QUERY_PLACEHOLDER_TEXT,
} from '@/app/components/base/prompt-editor/constants'
import type { InputVar } from '@/app/components/workflow/types'
import { InputVarType } from '@/app/components/workflow/types'
import { getMaxVarNameLength, MARKETPLACE_URL_PREFIX, MAX_VAR_KEY_LENGTH, VAR_ITEM_TEMPLATE, VAR_ITEM_TEMPLATE_IN_WORKFLOW } from '@/config'
const otherAllowedRegex = /^\w+$/
@@ -97,7 +97,7 @@ export const hasDuplicateStr = (strArr: string[]) => {
return !!Object.keys(strObj).find(key => strObj[key] > 1)
}
const varRegex = /\{\{([a-zA-Z_]\w*)\}\}/g
const varRegex = /\{\{([a-z_]\w*)\}\}/gi
export const getVars = (value: string) => {
if (!value)
return []

View File

@@ -1,4 +1,4 @@
import { ZodError, z } from 'zod'
import { z, ZodError } from 'zod'
describe('Zod Features', () => {
it('should support string', () => {