mirror of
https://gitee.com/newgateway/vtj.git
synced 2026-06-23 11:43:17 +08:00
305 lines
8.7 KiB
TypeScript
305 lines
8.7 KiB
TypeScript
import { expect, describe, test } from 'vitest';
|
|
import {
|
|
isJSExpression,
|
|
isJSFunction,
|
|
isJSCode,
|
|
JSCodeToString,
|
|
replaceThis,
|
|
replaceContext,
|
|
parseValue,
|
|
replaceComputedValue,
|
|
replaceFunctionTag,
|
|
parsePlainObjectValue,
|
|
getModifiers,
|
|
jsonToStyle,
|
|
skipUniComponents,
|
|
encodeDataSource,
|
|
decodeDataSource
|
|
} from '../src/utils';
|
|
|
|
describe('isJSExpression', () => {
|
|
test('should return true for valid JSExpression', () => {
|
|
expect(isJSExpression({ type: 'JSExpression', value: '1 + 1' })).toBe(true);
|
|
});
|
|
|
|
test('should return falsy for null', () => {
|
|
expect(isJSExpression(null)).toBeFalsy();
|
|
expect(isJSExpression(undefined)).toBeFalsy();
|
|
});
|
|
|
|
test('should return false for plain objects', () => {
|
|
expect(isJSExpression({})).toBe(false);
|
|
expect(isJSExpression({ type: 'other' })).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('isJSFunction', () => {
|
|
test('should return true for valid JSFunction', () => {
|
|
expect(isJSFunction({ type: 'JSFunction', value: '() => {}' })).toBe(true);
|
|
});
|
|
|
|
test('should return falsy for null', () => {
|
|
expect(isJSFunction(null)).toBeFalsy();
|
|
expect(isJSFunction(undefined)).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
describe('isJSCode', () => {
|
|
test('should return true for JSExpression or JSFunction', () => {
|
|
expect(isJSCode({ type: 'JSExpression', value: '' })).toBe(true);
|
|
expect(isJSCode({ type: 'JSFunction', value: '' })).toBe(true);
|
|
});
|
|
|
|
test('should return falsy for null', () => {
|
|
expect(isJSCode(null)).toBeFalsy();
|
|
expect(isJSCode('string')).toBe(false);
|
|
expect(isJSCode(123)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('JSCodeToString', () => {
|
|
test('should return value for JS code types', () => {
|
|
expect(JSCodeToString({ type: 'JSExpression', value: 'a + b' })).toBe(
|
|
'a + b'
|
|
);
|
|
expect(JSCodeToString({ type: 'JSFunction', value: '() => {}' })).toBe(
|
|
'() => {}'
|
|
);
|
|
});
|
|
|
|
test('should stringify plain values', () => {
|
|
expect(JSCodeToString(42)).toBe('42');
|
|
expect(JSCodeToString('hello')).toBe('"hello"');
|
|
expect(JSCodeToString({ key: 'val' })).toBe('{"key":"val"}');
|
|
expect(JSCodeToString([1, 2])).toBe('[1,2]');
|
|
});
|
|
});
|
|
|
|
describe('replaceThis', () => {
|
|
test('should replace this. with empty string', () => {
|
|
expect(replaceThis('this.count')).toBe('count');
|
|
expect(replaceThis('this.state.foo')).toBe('state.foo');
|
|
});
|
|
|
|
test('should replace multiple occurrences', () => {
|
|
expect(replaceThis('this.a + this.b')).toBe('a + b');
|
|
});
|
|
|
|
test('should handle empty string', () => {
|
|
expect(replaceThis('')).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('replaceContext', () => {
|
|
test('should replace this.context?. with empty string', () => {
|
|
expect(replaceContext('this.context?.item')).toBe('item');
|
|
expect(replaceContext('this.context?.item.title')).toBe('item.title');
|
|
});
|
|
|
|
test('should handle this.context. without optional chaining', () => {
|
|
expect(replaceContext('this.context.item')).toBe('item');
|
|
});
|
|
|
|
test('should not affect other this. references', () => {
|
|
const result = replaceContext('this.state.foo + this.context?.item');
|
|
expect(result).toBe('this.state.foo + item');
|
|
});
|
|
});
|
|
|
|
describe('parseValue', () => {
|
|
const computedKeys = ['total'];
|
|
|
|
test('should parse JSExpression value', () => {
|
|
const result = parseValue({
|
|
type: 'JSExpression',
|
|
value: 'this.count + 1'
|
|
});
|
|
expect(result).toBe('count + 1');
|
|
});
|
|
|
|
test('should parse JSFunction value', () => {
|
|
const result = parseValue({ type: 'JSFunction', value: '() => {}' });
|
|
expect(result).toBe('() => {}');
|
|
});
|
|
|
|
test('should replace double quotes with single quotes for JS code when quote=true', () => {
|
|
const result = parseValue({
|
|
type: 'JSExpression',
|
|
value: '"hello world"'
|
|
});
|
|
expect(result).toBe("'hello world'");
|
|
});
|
|
|
|
test('should stringify plain values', () => {
|
|
expect(parseValue(42)).toBe('42');
|
|
expect(parseValue('hello')).toBe('"hello"');
|
|
});
|
|
|
|
test('should stringify as plain string when stringify=false for non-JS string', () => {
|
|
const result = parseValue(42, true);
|
|
expect(result).toBe('42');
|
|
});
|
|
|
|
test('should keep this. when noThis=false', () => {
|
|
const result = parseValue(
|
|
{ type: 'JSExpression', value: 'this.count' },
|
|
true,
|
|
false
|
|
);
|
|
expect(result).toBe('this.count');
|
|
});
|
|
|
|
test('should replace computed .value references', () => {
|
|
const result = parseValue(
|
|
{ type: 'JSExpression', value: 'this.total.value' },
|
|
true,
|
|
true,
|
|
computedKeys
|
|
);
|
|
expect(result).toBe('total');
|
|
});
|
|
|
|
test('should trim trailing semicolon', () => {
|
|
const result = parseValue({ type: 'JSExpression', value: 'count + 1;' });
|
|
expect(result).toBe('count + 1');
|
|
});
|
|
|
|
test('should not quote when quote=false', () => {
|
|
const result = parseValue(
|
|
{ type: 'JSExpression', value: '"hello"' },
|
|
true,
|
|
true,
|
|
[],
|
|
false
|
|
);
|
|
expect(result).toBe('"hello"');
|
|
});
|
|
});
|
|
|
|
describe('replaceComputedValue', () => {
|
|
const keys = ['total', 'count'];
|
|
|
|
test('should replace this.key.value with this.key', () => {
|
|
const result = replaceComputedValue('this.total.value + 1', keys);
|
|
expect(result).toBe('this.total + 1');
|
|
});
|
|
|
|
test('should replace multiple computed keys', () => {
|
|
const result = replaceComputedValue(
|
|
'this.total.value + this.count.value',
|
|
keys
|
|
);
|
|
expect(result).toBe('this.total + this.count');
|
|
});
|
|
|
|
test('should handle empty content', () => {
|
|
expect(replaceComputedValue('', keys)).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('replaceFunctionTag', () => {
|
|
test('should handle bracket-wrapped async function', () => {
|
|
const result = replaceFunctionTag('(async function() { return 1 })');
|
|
expect(result).toBe('async() { return 1 }');
|
|
});
|
|
|
|
test('should handle bracket-wrapped function', () => {
|
|
const result = replaceFunctionTag('(function() { return 1 })');
|
|
expect(result).toBe('() { return 1 }');
|
|
});
|
|
|
|
test('should handle arrow function in brackets', () => {
|
|
const result = replaceFunctionTag('(() => { return 1 })');
|
|
expect(result).toBe('() { return 1 }');
|
|
});
|
|
|
|
test('should handle async arrow function in brackets', () => {
|
|
const result = replaceFunctionTag('(async () => { return 1 })');
|
|
expect(result).toBe('async () { return 1 }');
|
|
});
|
|
|
|
test('should handle expression-bodied arrow function', () => {
|
|
const result = replaceFunctionTag('(() => 1)');
|
|
expect(result).toBe('() { return 1 }');
|
|
});
|
|
|
|
test('should return handler starting with { as-is', () => {
|
|
const result = replaceFunctionTag('{ console.log("test") }');
|
|
expect(result).toBe('{ console.log("test") }');
|
|
});
|
|
});
|
|
|
|
describe('parsePlainObjectValue', () => {
|
|
test('should parse object entries', () => {
|
|
const result = parsePlainObjectValue({
|
|
name: { type: 'JSExpression', value: 'this.getName()' },
|
|
age: 18
|
|
});
|
|
expect(result).toContain('"name": getName()');
|
|
expect(result).toContain('"age": 18');
|
|
});
|
|
|
|
test('should handle empty object', () => {
|
|
expect(parsePlainObjectValue({})).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('getModifiers', () => {
|
|
test('should return modifier keys', () => {
|
|
expect(getModifiers({ prevent: true, stop: true })).toEqual([
|
|
'prevent',
|
|
'stop'
|
|
]);
|
|
});
|
|
|
|
test('should return string modifiers with dots', () => {
|
|
expect(getModifiers({ prevent: true, stop: true }, true)).toEqual([
|
|
'.prevent',
|
|
'.stop'
|
|
]);
|
|
});
|
|
|
|
test('should handle empty modifiers', () => {
|
|
expect(getModifiers({})).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('jsonToStyle', () => {
|
|
test('should convert JSON to CSS string', () => {
|
|
const result = jsonToStyle({ color: 'red', 'font-size': '14px' });
|
|
expect(result).toBe('color: red;font-size: 14px;');
|
|
});
|
|
|
|
test('should handle empty object', () => {
|
|
expect(jsonToStyle({})).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('skipUniComponents', () => {
|
|
test('should filter out uni components', () => {
|
|
const components = ['div', 'span', 'View', 'Text'];
|
|
const uniComponents = ['View', 'Text', 'ScrollView'];
|
|
const result = skipUniComponents(components, uniComponents);
|
|
expect(result).toEqual(['div', 'span']);
|
|
});
|
|
|
|
test('should return all if no uni components', () => {
|
|
expect(skipUniComponents(['div', 'span'], [])).toEqual(['div', 'span']);
|
|
});
|
|
});
|
|
|
|
describe('encodeDataSource / decodeDataSource', () => {
|
|
test('should encode and decode data source', () => {
|
|
const schema = { type: 'api', url: '/test', method: 'GET' };
|
|
const encoded = encodeDataSource(schema as any);
|
|
expect(typeof encoded).toBe('string');
|
|
|
|
const decoded = decodeDataSource(encoded);
|
|
expect(decoded).toEqual(schema);
|
|
});
|
|
|
|
test('should return null for invalid encoded data', () => {
|
|
expect(decodeDataSource('invalid-base64!!!')).toBeNull();
|
|
});
|
|
});
|