| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- /**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
- import * as React from 'react';
- import { beforeEach, describe, expect, it, vi } from 'vitest';
- import { ValidateTrigger } from '@/types';
- import { toField, toFieldState } from '@/core/to-field';
- import { FormModel } from '@/core/form-model';
- import { FieldModel } from '@/core/field-model';
- describe('toField', () => {
- let formModel: FormModel;
- let fieldModel: FieldModel;
- beforeEach(() => {
- formModel = new FormModel();
- formModel.init({});
- fieldModel = formModel.createField('username') as FieldModel;
- });
- it('should convert FieldModel to Field', () => {
- const field = toField(fieldModel);
- expect(field).toBeDefined();
- expect(field.name).toBe('username');
- expect(field.value).toBeUndefined();
- });
- it('should expose name property from model', () => {
- const field = toField(fieldModel);
- expect(field.name).toBe(fieldModel.name);
- });
- it('should expose value property from model', () => {
- fieldModel.value = 'John';
- const field = toField(fieldModel);
- expect(field.value).toBe('John');
- expect(field.value).toBe(fieldModel.value);
- });
- describe('onChange', () => {
- it('should update model value with plain value', () => {
- const field = toField(fieldModel);
- field.onChange('Alice');
- expect(fieldModel.value).toBe('Alice');
- expect(field.value).toBe('Alice');
- });
- it('should handle React change event for input', () => {
- const field = toField(fieldModel);
- const mockEvent = {
- target: {
- value: 'Bob',
- },
- } as React.ChangeEvent<HTMLInputElement>;
- field.onChange(mockEvent);
- expect(fieldModel.value).toBe('Bob');
- });
- it('should handle React change event for checkbox (checked)', () => {
- const field = toField(fieldModel);
- const mockEvent = {
- target: {
- type: 'checkbox',
- checked: true,
- value: 'on',
- },
- } as React.ChangeEvent<HTMLInputElement>;
- field.onChange(mockEvent);
- expect(fieldModel.value).toBe(true);
- });
- it('should handle React change event for checkbox (unchecked)', () => {
- const field = toField(fieldModel);
- const mockEvent = {
- target: {
- type: 'checkbox',
- checked: false,
- value: 'on',
- },
- } as React.ChangeEvent<HTMLInputElement>;
- field.onChange(mockEvent);
- expect(fieldModel.value).toBe(false);
- });
- it('should handle numeric value', () => {
- const field = toField(fieldModel);
- field.onChange(42);
- expect(fieldModel.value).toBe(42);
- });
- it('should handle object value', () => {
- const field = toField(fieldModel);
- const objValue = { name: 'test', value: 123 };
- field.onChange(objValue);
- expect(fieldModel.value).toEqual(objValue);
- });
- it('should handle array value', () => {
- const field = toField(fieldModel);
- const arrValue = ['a', 'b', 'c'];
- field.onChange(arrValue);
- expect(fieldModel.value).toEqual(arrValue);
- });
- });
- describe('onBlur', () => {
- it('should call validate when validateTrigger is onBlur', () => {
- formModel.dispose();
- formModel = new FormModel();
- formModel.init({ validateTrigger: ValidateTrigger.onBlur });
- fieldModel = formModel.createField('username') as FieldModel;
- const validateSpy = vi.spyOn(fieldModel, 'validate');
- const field = toField(fieldModel);
- field.onBlur?.();
- expect(validateSpy).toHaveBeenCalled();
- });
- it('should not trigger validation when validateTrigger is not onBlur', () => {
- formModel.dispose();
- formModel = new FormModel();
- formModel.init({ validateTrigger: ValidateTrigger.onChange });
- fieldModel = formModel.createField('username') as FieldModel;
- const validateSpy = vi.spyOn(fieldModel, 'validate');
- const field = toField(fieldModel);
- field.onBlur?.();
- expect(validateSpy).not.toHaveBeenCalled();
- });
- it('should not trigger validation when validateTrigger is onSubmit', () => {
- formModel.dispose();
- formModel = new FormModel();
- formModel.init({ validateTrigger: ValidateTrigger.onSubmit });
- fieldModel = formModel.createField('username') as FieldModel;
- const validateSpy = vi.spyOn(fieldModel, 'validate');
- const field = toField(fieldModel);
- field.onBlur?.();
- expect(validateSpy).not.toHaveBeenCalled();
- });
- });
- describe('onFocus', () => {
- it('should set isTouched to true', () => {
- const field = toField(fieldModel);
- expect(fieldModel.state.isTouched).toBe(false);
- field.onFocus?.();
- expect(fieldModel.state.isTouched).toBe(true);
- });
- it('should set isTouched only once', () => {
- const field = toField(fieldModel);
- field.onFocus?.();
- expect(fieldModel.state.isTouched).toBe(true);
- field.onFocus?.();
- expect(fieldModel.state.isTouched).toBe(true);
- });
- });
- it('should expose key property (non-enumerable)', () => {
- const field = toField(fieldModel);
- expect((field as any).key).toBe(fieldModel.id);
- expect(Object.keys(field)).not.toContain('key');
- });
- it('should hide _fieldModel property (non-enumerable)', () => {
- const field = toField(fieldModel);
- expect((field as any)._fieldModel).toBe(fieldModel);
- expect(Object.keys(field)).not.toContain('_fieldModel');
- });
- it('should preserve reactivity through getters', () => {
- const field = toField(fieldModel);
- expect(field.name).toBe('username');
- expect(field.value).toBeUndefined();
- fieldModel.value = 'NewValue';
- expect(field.value).toBe('NewValue');
- });
- });
- describe('toFieldState', () => {
- let formModel: FormModel;
- let fieldModel: FieldModel;
- beforeEach(() => {
- formModel = new FormModel();
- formModel.init({});
- fieldModel = formModel.createField('username') as FieldModel;
- });
- it('should convert FieldModelState to FieldState', () => {
- const fieldState = toFieldState(fieldModel.state);
- expect(fieldState).toBeDefined();
- expect(fieldState.isTouched).toBe(false);
- expect(fieldState.isDirty).toBe(false);
- expect(fieldState.invalid).toBe(false);
- expect(fieldState.isValidating).toBe(false);
- });
- it('should reflect isTouched state', () => {
- const fieldState = toFieldState(fieldModel.state);
- expect(fieldState.isTouched).toBe(false);
- fieldModel.state.isTouched = true;
- expect(fieldState.isTouched).toBe(true);
- });
- it('should reflect isDirty state', () => {
- const fieldState = toFieldState(fieldModel.state);
- expect(fieldState.isDirty).toBe(false);
- // Manually set dirty state
- fieldModel.state.isDirty = true;
- expect(fieldState.isDirty).toBe(true);
- });
- it('should reflect invalid state', () => {
- const fieldState = toFieldState(fieldModel.state);
- expect(fieldState.invalid).toBe(false);
- fieldModel.state.invalid = true;
- expect(fieldState.invalid).toBe(true);
- });
- it('should reflect isValidating state', () => {
- const fieldState = toFieldState(fieldModel.state);
- expect(fieldState.isValidating).toBe(false);
- fieldModel.state.isValidating = true;
- expect(fieldState.isValidating).toBe(true);
- });
- it('should return errors as flat array', () => {
- const fieldState = toFieldState(fieldModel.state);
- expect(fieldState.errors).toBeUndefined();
- fieldModel.state.errors = {
- validate1: ['Error 1', 'Error 2'],
- validate2: ['Error 3'],
- };
- expect(fieldState.errors).toEqual(['Error 1', 'Error 2', 'Error 3']);
- });
- it('should return warnings as flat array', () => {
- const fieldState = toFieldState(fieldModel.state);
- expect(fieldState.warnings).toBeUndefined();
- fieldModel.state.warnings = {
- validate1: ['Warning 1', 'Warning 2'],
- validate2: ['Warning 3'],
- };
- expect(fieldState.warnings).toEqual(['Warning 1', 'Warning 2', 'Warning 3']);
- });
- it('should handle empty errors object', () => {
- const fieldState = toFieldState(fieldModel.state);
- fieldModel.state.errors = {};
- expect(fieldState.errors).toEqual([]);
- });
- it('should handle empty warnings object', () => {
- const fieldState = toFieldState(fieldModel.state);
- fieldModel.state.warnings = {};
- expect(fieldState.warnings).toEqual([]);
- });
- it('should preserve reactivity through getters', () => {
- const fieldState = toFieldState(fieldModel.state);
- expect(fieldState.isTouched).toBe(false);
- fieldModel.state.isTouched = true;
- expect(fieldState.isTouched).toBe(true);
- });
- });
|