test: create some hooks and utils test script, modified clipboard test script (#27928)

This commit is contained in:
Gritty_dev
2025-11-12 08:47:06 -05:00
committed by GitHub
parent 19c92fd670
commit 5c06e285ec
9 changed files with 2289 additions and 3 deletions

View File

@@ -1,3 +1,13 @@
/**
* Test suite for clipboard utilities
*
* This module provides cross-browser clipboard functionality with automatic fallback:
* 1. Modern Clipboard API (navigator.clipboard.writeText) - preferred method
* 2. Legacy execCommand('copy') - fallback for older browsers
*
* The implementation ensures clipboard operations work across all supported browsers
* while gracefully handling permissions and API availability.
*/
import { writeTextToClipboard } from './clipboard'
describe('Clipboard Utilities', () => {
@@ -6,6 +16,10 @@ describe('Clipboard Utilities', () => {
jest.restoreAllMocks()
})
/**
* Test modern Clipboard API usage
* When navigator.clipboard is available, should use the modern API
*/
it('should use navigator.clipboard.writeText when available', async () => {
const mockWriteText = jest.fn().mockResolvedValue(undefined)
Object.defineProperty(navigator, 'clipboard', {
@@ -18,6 +32,11 @@ describe('Clipboard Utilities', () => {
expect(mockWriteText).toHaveBeenCalledWith('test text')
})
/**
* Test fallback to legacy execCommand method
* When Clipboard API is unavailable, should use document.execCommand('copy')
* This involves creating a temporary textarea element
*/
it('should fallback to execCommand when clipboard API not available', async () => {
Object.defineProperty(navigator, 'clipboard', {
value: undefined,
@@ -38,6 +57,10 @@ describe('Clipboard Utilities', () => {
expect(removeChildSpy).toHaveBeenCalled()
})
/**
* Test error handling when execCommand returns false
* execCommand returns false when the operation fails
*/
it('should handle execCommand failure', async () => {
Object.defineProperty(navigator, 'clipboard', {
value: undefined,
@@ -51,6 +74,10 @@ describe('Clipboard Utilities', () => {
await expect(writeTextToClipboard('fail text')).rejects.toThrow()
})
/**
* Test error handling when execCommand throws an exception
* Should propagate the error to the caller
*/
it('should handle execCommand exception', async () => {
Object.defineProperty(navigator, 'clipboard', {
value: undefined,
@@ -66,6 +93,10 @@ describe('Clipboard Utilities', () => {
await expect(writeTextToClipboard('error text')).rejects.toThrow('execCommand error')
})
/**
* Test proper cleanup of temporary DOM elements
* The temporary textarea should be removed after copying
*/
it('should clean up textarea after fallback', async () => {
Object.defineProperty(navigator, 'clipboard', {
value: undefined,
@@ -81,6 +112,10 @@ describe('Clipboard Utilities', () => {
expect(removeChildSpy).toHaveBeenCalled()
})
/**
* Test copying empty strings
* Should handle edge case of empty clipboard content
*/
it('should handle empty string', async () => {
const mockWriteText = jest.fn().mockResolvedValue(undefined)
Object.defineProperty(navigator, 'clipboard', {
@@ -93,6 +128,10 @@ describe('Clipboard Utilities', () => {
expect(mockWriteText).toHaveBeenCalledWith('')
})
/**
* Test copying text with special characters
* Should preserve newlines, tabs, quotes, unicode, and emojis
*/
it('should handle special characters', async () => {
const mockWriteText = jest.fn().mockResolvedValue(undefined)
Object.defineProperty(navigator, 'clipboard', {

253
web/utils/context.spec.ts Normal file
View File

@@ -0,0 +1,253 @@
/**
* Test suite for React context creation utilities
*
* This module provides helper functions to create React contexts with better type safety
* and automatic error handling when context is used outside of its provider.
*
* Two variants are provided:
* - createCtx: Standard React context using useContext/createContext
* - 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', () => {
describe('createCtx', () => {
/**
* Test that createCtx creates a valid context with provider and hook
* The function should return a tuple with [Provider, useContextValue, Context]
* plus named properties for easier access
*/
it('should create context with provider and hook', () => {
type TestContextValue = { value: string }
const [Provider, useTestContext, Context] = createCtx<TestContextValue>({
name: 'Test',
})
expect(Provider).toBeDefined()
expect(useTestContext).toBeDefined()
expect(Context).toBeDefined()
})
/**
* Test that the context hook returns the provided value correctly
* when used within the context provider
*/
it('should provide and consume context value', () => {
type TestContextValue = { value: string }
const [Provider, useTestContext] = createCtx<TestContextValue>({
name: 'Test',
})
const testValue = { value: 'test-value' }
const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(Provider, { value: testValue }, children)
const { result } = renderHook(() => useTestContext(), { wrapper })
expect(result.current).toEqual(testValue)
})
/**
* Test that accessing context outside of provider throws an error
* This ensures developers are notified when they forget to wrap components
*/
it('should throw error when used outside provider', () => {
type TestContextValue = { value: string }
const [, useTestContext] = createCtx<TestContextValue>({
name: 'Test',
})
// Suppress console.error for this test
const consoleError = jest.spyOn(console, 'error').mockImplementation(() => { /* suppress error */ })
expect(() => {
renderHook(() => useTestContext())
}).toThrow('No Test context found.')
consoleError.mockRestore()
})
/**
* Test that context works with default values
* When a default value is provided, it should be accessible without a provider
*/
it('should use default value when provided', () => {
type TestContextValue = { value: string }
const defaultValue = { value: 'default' }
const [, useTestContext] = createCtx<TestContextValue>({
name: 'Test',
defaultValue,
})
const { result } = renderHook(() => useTestContext())
expect(result.current).toEqual(defaultValue)
})
/**
* Test that the returned tuple has named properties for convenience
* This allows destructuring or property access based on preference
*/
it('should expose named properties', () => {
type TestContextValue = { value: string }
const result = createCtx<TestContextValue>({ name: 'Test' })
expect(result.provider).toBe(result[0])
expect(result.useContextValue).toBe(result[1])
expect(result.context).toBe(result[2])
})
/**
* Test context with complex data types
* Ensures type safety is maintained with nested objects and arrays
*/
it('should handle complex context values', () => {
type ComplexContext = {
user: { id: string; name: string }
settings: { theme: string; locale: string }
actions: Array<() => void>
}
const [Provider, useComplexContext] = createCtx<ComplexContext>({
name: 'Complex',
})
const complexValue: ComplexContext = {
user: { id: '123', name: 'Test User' },
settings: { theme: 'dark', locale: 'en-US' },
actions: [
() => { /* empty action 1 */ },
() => { /* empty action 2 */ },
],
}
const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(Provider, { value: complexValue }, children)
const { result } = renderHook(() => useComplexContext(), { wrapper })
expect(result.current).toEqual(complexValue)
expect(result.current.user.id).toBe('123')
expect(result.current.settings.theme).toBe('dark')
expect(result.current.actions).toHaveLength(2)
})
/**
* Test that context updates propagate to consumers
* When provider value changes, hooks should receive the new value
*/
it('should update when context value changes', () => {
type TestContextValue = { count: number }
const [Provider, useTestContext] = createCtx<TestContextValue>({
name: 'Test',
})
let value = { count: 0 }
const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(Provider, { value }, children)
const { result, rerender } = renderHook(() => useTestContext(), { wrapper })
expect(result.current.count).toBe(0)
value = { count: 5 }
rerender()
expect(result.current.count).toBe(5)
})
})
describe('createSelectorCtx', () => {
/**
* Test that createSelectorCtx creates a valid context with selector support
* This variant uses use-context-selector for optimized re-renders
*/
it('should create selector context with provider and hook', () => {
type TestContextValue = { value: string }
const [Provider, useTestContext, Context] = createSelectorCtx<TestContextValue>({
name: 'SelectorTest',
})
expect(Provider).toBeDefined()
expect(useTestContext).toBeDefined()
expect(Context).toBeDefined()
})
/**
* Test that selector context provides and consumes values correctly
* The API should be identical to createCtx for basic usage
*/
it('should provide and consume context value with selector', () => {
type TestContextValue = { value: string }
const [Provider, useTestContext] = createSelectorCtx<TestContextValue>({
name: 'SelectorTest',
})
const testValue = { value: 'selector-test' }
const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(Provider, { value: testValue }, children)
const { result } = renderHook(() => useTestContext(), { wrapper })
expect(result.current).toEqual(testValue)
})
/**
* Test error handling for selector context
* Should throw error when used outside provider, same as createCtx
*/
it('should throw error when used outside provider', () => {
type TestContextValue = { value: string }
const [, useTestContext] = createSelectorCtx<TestContextValue>({
name: 'SelectorTest',
})
const consoleError = jest.spyOn(console, 'error').mockImplementation(() => { /* suppress error */ })
expect(() => {
renderHook(() => useTestContext())
}).toThrow('No SelectorTest context found.')
consoleError.mockRestore()
})
/**
* Test that selector context works with default values
*/
it('should use default value when provided', () => {
type TestContextValue = { value: string }
const defaultValue = { value: 'selector-default' }
const [, useTestContext] = createSelectorCtx<TestContextValue>({
name: 'SelectorTest',
defaultValue,
})
const { result } = renderHook(() => useTestContext())
expect(result.current).toEqual(defaultValue)
})
})
describe('Context without name', () => {
/**
* Test that contexts can be created without a name
* The error message should use a generic fallback
*/
it('should create context without name and show generic error', () => {
type TestContextValue = { value: string }
const [, useTestContext] = createCtx<TestContextValue>()
const consoleError = jest.spyOn(console, 'error').mockImplementation(() => { /* suppress error */ })
expect(() => {
renderHook(() => useTestContext())
}).toThrow('No related context found.')
consoleError.mockRestore()
})
})
})

View File

@@ -0,0 +1,819 @@
/**
* Test suite for model configuration transformation utilities
*
* This module handles the conversion between two different representations of user input forms:
* 1. UserInputFormItem: The form structure used in the UI
* 2. PromptVariable: The variable structure used in prompts and model configuration
*
* Key functions:
* - userInputsFormToPromptVariables: Converts UI form items to prompt variables
* - promptVariablesToUserInputsForm: Converts prompt variables back to form items
* - formatBooleanInputs: Ensures boolean inputs are properly typed
*/
import {
formatBooleanInputs,
promptVariablesToUserInputsForm,
userInputsFormToPromptVariables,
} from './model-config'
import type { UserInputFormItem } from '@/types/app'
import type { PromptVariable } from '@/models/debug'
describe('Model Config Utilities', () => {
describe('userInputsFormToPromptVariables', () => {
/**
* Test handling of null or undefined input
* Should return empty array when no inputs provided
*/
it('should return empty array for null input', () => {
const result = userInputsFormToPromptVariables(null)
expect(result).toEqual([])
})
/**
* Test conversion of text-input (string) type
* Text inputs are the most common form field type
*/
it('should convert text-input to string prompt variable', () => {
const userInputs: UserInputFormItem[] = [
{
'text-input': {
label: 'User Name',
variable: 'user_name',
required: true,
max_length: 100,
default: '',
hide: false,
},
},
]
const result = userInputsFormToPromptVariables(userInputs)
expect(result).toHaveLength(1)
expect(result[0]).toEqual({
key: 'user_name',
name: 'User Name',
required: true,
type: 'string',
max_length: 100,
options: [],
is_context_var: false,
hide: false,
default: '',
})
})
/**
* Test conversion of paragraph type
* Paragraphs are multi-line text inputs
*/
it('should convert paragraph to paragraph prompt variable', () => {
const userInputs: UserInputFormItem[] = [
{
paragraph: {
label: 'Description',
variable: 'description',
required: false,
max_length: 500,
default: '',
hide: false,
},
},
]
const result = userInputsFormToPromptVariables(userInputs)
expect(result[0]).toEqual({
key: 'description',
name: 'Description',
required: false,
type: 'paragraph',
max_length: 500,
options: [],
is_context_var: false,
hide: false,
default: '',
})
})
/**
* Test conversion of number type
* Number inputs should preserve numeric constraints
*/
it('should convert number input to number prompt variable', () => {
const userInputs: UserInputFormItem[] = [
{
number: {
label: 'Age',
variable: 'age',
required: true,
default: '',
hide: false,
},
} as any,
]
const result = userInputsFormToPromptVariables(userInputs)
expect(result[0]).toEqual({
key: 'age',
name: 'Age',
required: true,
type: 'number',
options: [],
hide: false,
default: '',
})
})
/**
* Test conversion of checkbox (boolean) type
* Checkboxes are converted to 'checkbox' type in prompt variables
*/
it('should convert checkbox to checkbox prompt variable', () => {
const userInputs: UserInputFormItem[] = [
{
checkbox: {
label: 'Accept Terms',
variable: 'accept_terms',
required: true,
default: '',
hide: false,
},
} as any,
]
const result = userInputsFormToPromptVariables(userInputs)
expect(result[0]).toEqual({
key: 'accept_terms',
name: 'Accept Terms',
required: true,
type: 'checkbox',
options: [],
hide: false,
default: '',
})
})
/**
* Test conversion of select (dropdown) type
* Select inputs include options array
*/
it('should convert select input to select prompt variable', () => {
const userInputs: UserInputFormItem[] = [
{
select: {
label: 'Country',
variable: 'country',
required: true,
options: ['USA', 'Canada', 'Mexico'],
default: 'USA',
hide: false,
},
},
]
const result = userInputsFormToPromptVariables(userInputs)
expect(result[0]).toEqual({
key: 'country',
name: 'Country',
required: true,
type: 'select',
options: ['USA', 'Canada', 'Mexico'],
is_context_var: false,
hide: false,
default: 'USA',
})
})
/**
* Test conversion of file upload type
* File inputs include configuration for allowed types and upload methods
*/
it('should convert file input to file prompt variable', () => {
const userInputs: UserInputFormItem[] = [
{
file: {
label: 'Profile Picture',
variable: 'profile_pic',
required: false,
allowed_file_types: ['image'],
allowed_file_extensions: ['.jpg', '.png'],
allowed_file_upload_methods: ['local_file', 'remote_url'],
default: '',
hide: false,
},
} as any,
]
const result = userInputsFormToPromptVariables(userInputs)
expect(result[0]).toEqual({
key: 'profile_pic',
name: 'Profile Picture',
required: false,
type: 'file',
config: {
allowed_file_types: ['image'],
allowed_file_extensions: ['.jpg', '.png'],
allowed_file_upload_methods: ['local_file', 'remote_url'],
number_limits: 1,
},
hide: false,
default: '',
})
})
/**
* Test conversion of file-list type
* File lists allow multiple file uploads with a max_length constraint
*/
it('should convert file-list input to file-list prompt variable', () => {
const userInputs: UserInputFormItem[] = [
{
'file-list': {
label: 'Documents',
variable: 'documents',
required: true,
allowed_file_types: ['document'],
allowed_file_extensions: ['.pdf', '.docx'],
allowed_file_upload_methods: ['local_file'],
max_length: 5,
default: '',
hide: false,
},
} as any,
]
const result = userInputsFormToPromptVariables(userInputs)
expect(result[0]).toEqual({
key: 'documents',
name: 'Documents',
required: true,
type: 'file-list',
config: {
allowed_file_types: ['document'],
allowed_file_extensions: ['.pdf', '.docx'],
allowed_file_upload_methods: ['local_file'],
number_limits: 5,
},
hide: false,
default: '',
})
})
/**
* Test conversion of external_data_tool type
* External data tools have custom configuration and icons
*/
it('should convert external_data_tool to prompt variable', () => {
const userInputs: UserInputFormItem[] = [
{
external_data_tool: {
label: 'API Data',
variable: 'api_data',
type: 'api',
enabled: true,
required: false,
config: { endpoint: 'https://api.example.com' },
icon: 'api-icon',
icon_background: '#FF5733',
hide: false,
},
} as any,
]
const result = userInputsFormToPromptVariables(userInputs)
expect(result[0]).toEqual({
key: 'api_data',
name: 'API Data',
required: false,
type: 'api',
enabled: true,
config: { endpoint: 'https://api.example.com' },
icon: 'api-icon',
icon_background: '#FF5733',
is_context_var: false,
hide: false,
})
})
/**
* Test handling of dataset_query_variable
* When a variable matches the dataset_query_variable, is_context_var should be true
*/
it('should mark variable as context var when matching dataset_query_variable', () => {
const userInputs: UserInputFormItem[] = [
{
'text-input': {
label: 'Query',
variable: 'query',
required: true,
max_length: 200,
default: '',
hide: false,
},
},
]
const result = userInputsFormToPromptVariables(userInputs, 'query')
expect(result[0].is_context_var).toBe(true)
})
/**
* Test conversion of multiple mixed input types
* Should handle an array with different input types correctly
*/
it('should convert multiple mixed input types', () => {
const userInputs: UserInputFormItem[] = [
{
'text-input': {
label: 'Name',
variable: 'name',
required: true,
max_length: 50,
default: '',
hide: false,
},
},
{
number: {
label: 'Age',
variable: 'age',
required: false,
default: '',
hide: false,
},
} as any,
{
select: {
label: 'Gender',
variable: 'gender',
required: true,
options: ['Male', 'Female', 'Other'],
default: '',
hide: false,
},
},
]
const result = userInputsFormToPromptVariables(userInputs)
expect(result).toHaveLength(3)
expect(result[0].type).toBe('string')
expect(result[1].type).toBe('number')
expect(result[2].type).toBe('select')
})
})
describe('promptVariablesToUserInputsForm', () => {
/**
* Test conversion of string prompt variable back to text-input
*/
it('should convert string prompt variable to text-input', () => {
const promptVariables: PromptVariable[] = [
{
key: 'user_name',
name: 'User Name',
required: true,
type: 'string',
max_length: 100,
options: [],
},
]
const result = promptVariablesToUserInputsForm(promptVariables)
expect(result).toHaveLength(1)
expect(result[0]).toEqual({
'text-input': {
label: 'User Name',
variable: 'user_name',
required: true,
max_length: 100,
default: '',
hide: undefined,
},
})
})
/**
* Test conversion of paragraph prompt variable
*/
it('should convert paragraph prompt variable to paragraph input', () => {
const promptVariables: PromptVariable[] = [
{
key: 'description',
name: 'Description',
required: false,
type: 'paragraph',
max_length: 500,
options: [],
},
]
const result = promptVariablesToUserInputsForm(promptVariables)
expect(result[0]).toEqual({
paragraph: {
label: 'Description',
variable: 'description',
required: false,
max_length: 500,
default: '',
hide: undefined,
},
})
})
/**
* Test conversion of number prompt variable
*/
it('should convert number prompt variable to number input', () => {
const promptVariables: PromptVariable[] = [
{
key: 'age',
name: 'Age',
required: true,
type: 'number',
options: [],
},
]
const result = promptVariablesToUserInputsForm(promptVariables)
expect(result[0]).toEqual({
number: {
label: 'Age',
variable: 'age',
required: true,
default: '',
hide: undefined,
},
})
})
/**
* Test conversion of checkbox prompt variable
*/
it('should convert checkbox prompt variable to checkbox input', () => {
const promptVariables: PromptVariable[] = [
{
key: 'accept_terms',
name: 'Accept Terms',
required: true,
type: 'checkbox',
options: [],
},
]
const result = promptVariablesToUserInputsForm(promptVariables)
expect(result[0]).toEqual({
checkbox: {
label: 'Accept Terms',
variable: 'accept_terms',
required: true,
default: '',
hide: undefined,
},
})
})
/**
* Test conversion of select prompt variable
*/
it('should convert select prompt variable to select input', () => {
const promptVariables: PromptVariable[] = [
{
key: 'country',
name: 'Country',
required: true,
type: 'select',
options: ['USA', 'Canada', 'Mexico'],
default: 'USA',
},
]
const result = promptVariablesToUserInputsForm(promptVariables)
expect(result[0]).toEqual({
select: {
label: 'Country',
variable: 'country',
required: true,
options: ['USA', 'Canada', 'Mexico'],
default: 'USA',
hide: undefined,
},
})
})
/**
* Test filtering of invalid prompt variables
* Variables without key or name should be filtered out
*/
it('should filter out variables with empty key or name', () => {
const promptVariables: PromptVariable[] = [
{
key: '',
name: 'Empty Key',
required: true,
type: 'string',
options: [],
},
{
key: 'valid',
name: '',
required: true,
type: 'string',
options: [],
},
{
key: ' ',
name: 'Whitespace Key',
required: true,
type: 'string',
options: [],
},
{
key: 'valid_key',
name: 'Valid Name',
required: true,
type: 'string',
options: [],
},
]
const result = promptVariablesToUserInputsForm(promptVariables)
expect(result).toHaveLength(1)
expect((result[0] as any)['text-input']?.variable).toBe('valid_key')
})
/**
* Test conversion of external data tool prompt variable
*/
it('should convert external data tool prompt variable', () => {
const promptVariables: PromptVariable[] = [
{
key: 'api_data',
name: 'API Data',
required: false,
type: 'api',
enabled: true,
config: { endpoint: 'https://api.example.com' },
icon: 'api-icon',
icon_background: '#FF5733',
},
]
const result = promptVariablesToUserInputsForm(promptVariables)
expect(result[0]).toEqual({
external_data_tool: {
label: 'API Data',
variable: 'api_data',
enabled: true,
type: 'api',
config: { endpoint: 'https://api.example.com' },
required: false,
icon: 'api-icon',
icon_background: '#FF5733',
hide: undefined,
},
})
})
/**
* Test that required defaults to true when not explicitly set to false
*/
it('should default required to true when not false', () => {
const promptVariables: PromptVariable[] = [
{
key: 'test1',
name: 'Test 1',
required: undefined,
type: 'string',
options: [],
},
{
key: 'test2',
name: 'Test 2',
required: false,
type: 'string',
options: [],
},
]
const result = promptVariablesToUserInputsForm(promptVariables)
expect((result[0] as any)['text-input']?.required).toBe(true)
expect((result[1] as any)['text-input']?.required).toBe(false)
})
})
describe('formatBooleanInputs', () => {
/**
* Test that null or undefined inputs are handled gracefully
*/
it('should return inputs unchanged when useInputs is null', () => {
const inputs = { key1: 'value1', key2: 'value2' }
const result = formatBooleanInputs(null, inputs)
expect(result).toEqual(inputs)
})
it('should return inputs unchanged when useInputs is undefined', () => {
const inputs = { key1: 'value1', key2: 'value2' }
const result = formatBooleanInputs(undefined, inputs)
expect(result).toEqual(inputs)
})
/**
* Test conversion of boolean input values to actual boolean type
* This is important for proper type handling in the backend
* Note: checkbox inputs are converted to type 'checkbox' by userInputsFormToPromptVariables
*/
it('should convert boolean inputs to boolean type', () => {
const useInputs: PromptVariable[] = [
{
key: 'accept_terms',
name: 'Accept Terms',
required: true,
type: 'checkbox',
options: [],
},
{
key: 'subscribe',
name: 'Subscribe',
required: false,
type: 'checkbox',
options: [],
},
]
const inputs = {
accept_terms: 'true',
subscribe: '',
other_field: 'value',
}
const result = formatBooleanInputs(useInputs, inputs)
expect(result).toEqual({
accept_terms: true,
subscribe: false,
other_field: 'value',
})
})
/**
* Test that non-boolean inputs are not affected
*/
it('should not modify non-boolean inputs', () => {
const useInputs: PromptVariable[] = [
{
key: 'name',
name: 'Name',
required: true,
type: 'string',
options: [],
},
{
key: 'age',
name: 'Age',
required: true,
type: 'number',
options: [],
},
]
const inputs = {
name: 'John Doe',
age: 30,
}
const result = formatBooleanInputs(useInputs, inputs)
expect(result).toEqual(inputs)
})
/**
* Test handling of truthy and falsy values for boolean conversion
* Note: checkbox inputs are converted to type 'checkbox' by userInputsFormToPromptVariables
*/
it('should handle various truthy and falsy values', () => {
const useInputs: PromptVariable[] = [
{
key: 'bool1',
name: 'Bool 1',
required: true,
type: 'checkbox',
options: [],
},
{
key: 'bool2',
name: 'Bool 2',
required: true,
type: 'checkbox',
options: [],
},
{
key: 'bool3',
name: 'Bool 3',
required: true,
type: 'checkbox',
options: [],
},
{
key: 'bool4',
name: 'Bool 4',
required: true,
type: 'checkbox',
options: [],
},
]
const inputs = {
bool1: 1,
bool2: 0,
bool3: 'yes',
bool4: null as any,
}
const result = formatBooleanInputs(useInputs, inputs)
expect(result?.bool1).toBe(true)
expect(result?.bool2).toBe(false)
expect(result?.bool3).toBe(true)
expect(result?.bool4).toBe(false)
})
/**
* Test that the function creates a new object and doesn't mutate the original
* Note: checkbox inputs are converted to type 'checkbox' by userInputsFormToPromptVariables
*/
it('should not mutate original inputs object', () => {
const useInputs: PromptVariable[] = [
{
key: 'flag',
name: 'Flag',
required: true,
type: 'checkbox',
options: [],
},
]
const inputs = { flag: 'true', other: 'value' }
const originalInputs = { ...inputs }
formatBooleanInputs(useInputs, inputs)
expect(inputs).toEqual(originalInputs)
})
})
describe('Round-trip conversion', () => {
/**
* Test that converting from UserInputForm to PromptVariable and back
* preserves the essential data (though some fields may have defaults applied)
*/
it('should preserve data through round-trip conversion', () => {
const originalUserInputs: UserInputFormItem[] = [
{
'text-input': {
label: 'Name',
variable: 'name',
required: true,
max_length: 50,
default: '',
hide: false,
},
},
{
select: {
label: 'Type',
variable: 'type',
required: false,
options: ['A', 'B', 'C'],
default: 'A',
hide: false,
},
},
]
const promptVars = userInputsFormToPromptVariables(originalUserInputs)
const backToUserInputs = promptVariablesToUserInputsForm(promptVars)
expect(backToUserInputs).toHaveLength(2)
expect((backToUserInputs[0] as any)['text-input']?.variable).toBe('name')
expect((backToUserInputs[1] as any).select?.variable).toBe('type')
expect((backToUserInputs[1] as any).select?.options).toEqual(['A', 'B', 'C'])
})
})
})

View File

@@ -200,7 +200,7 @@ export const formatBooleanInputs = (useInputs?: PromptVariable[] | null, inputs?
return inputs
const res = { ...inputs }
useInputs.forEach((item) => {
const isBooleanInput = item.type === 'boolean'
const isBooleanInput = item.type === 'checkbox'
if (isBooleanInput) {
// Convert boolean inputs to boolean type
res[item.key] = !!res[item.key]