From b1f020d53242981751c4e941a0fde9d61e9d6060 Mon Sep 17 00:00:00 2001 From: roymondchen Date: Mon, 9 Jun 2025 14:18:36 +0800 Subject: [PATCH] =?UTF-8?q?fix(data-source,utils):=20=E4=B8=8D=E4=BD=BF?= =?UTF-8?q?=E7=94=A8replaceAll,=E5=AE=89=E5=8D=9310=E4=B8=8D=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/data-source/src/utils.ts | 2 +- packages/data-source/tests/utils.spec.ts | 118 +++++++++++++++++++++++ packages/utils/src/index.ts | 2 +- packages/utils/tests/unit/index.spec.ts | 66 +++++++++++++ 4 files changed, 186 insertions(+), 2 deletions(-) diff --git a/packages/data-source/src/utils.ts b/packages/data-source/src/utils.ts index 3192a942..2bb6fbe2 100644 --- a/packages/data-source/src/utils.ts +++ b/packages/data-source/src/utils.ts @@ -149,7 +149,7 @@ export const compliedDataSourceField = (value: any, data: DataSourceManagerData) }; export const template = (value: string, data?: DataSourceManagerData) => - value.replaceAll(dataSourceTemplateRegExp, (match, $1) => { + value.replace(dataSourceTemplateRegExp, (match, $1) => { try { return getValueByKeyPath($1, data); // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/data-source/tests/utils.spec.ts b/packages/data-source/tests/utils.spec.ts index b9ca2666..2265e2d3 100644 --- a/packages/data-source/tests/utils.spec.ts +++ b/packages/data-source/tests/utils.spec.ts @@ -1,5 +1,7 @@ import { describe, expect, test } from 'vitest'; +import { dataSourceTemplateRegExp } from '@tmagic/core'; + import { compiledCondition, createIteratorContentData, template } from '@data-source/utils'; describe('compiledCondition', () => { @@ -39,6 +41,122 @@ describe('template', () => { const value = template('xxx${aa.bb}123${aa1.bb1}dsf', { aa: { bb: 1 }, aa1: { bb1: 2 } }); expect(value).toBe('xxx11232dsf'); }); + + // 测试1: 普通字符串,没有模板变量 + test('should return original string when no template variables', () => { + const value = 'This is a plain text'; + const result = template(value); + expect(result).toBe(value); + }); + + // 测试2: 包含模板变量但未提供数据源 + test('should keep template variables when no data provided', () => { + const value = 'Hello ${user.name}'; + const result = template(value); + expect(result).toBe(value); + }); + + // 测试3: 包含模板变量且数据源中有对应值 + test('should replace template variables with data source values', () => { + const value = 'Hello ${user.name}, your age is ${user.age}'; + const data = { + user: { + name: 'John', + age: 30, + }, + }; + const result = template(value, data); + expect(result).toBe('Hello John, your age is 30'); + }); + + // 测试4: 模板变量在数据源中不存在 + test('should keep template variables when path not found in data', () => { + const value = 'Hello ${user.name}, your job is ${user.job}'; + const data = { + user: { + name: 'John', + }, + }; + const result = template(value, data); + expect(result).toBe('Hello John, your job is undefined'); + }); + + // 测试5: 多层嵌套的数据源路径 + test('should handle deeply nested data paths', () => { + const value = 'User info: ${user.personal.details.address.city}'; + const data = { + user: { + personal: { + details: { + address: { + city: 'Beijing', + }, + }, + }, + }, + }; + const result = template(value, data); + expect(result).toBe('User info: Beijing'); + }); + + // 测试6: 多个模板变量混合的情况 + test('should handle multiple template variables mixed with text', () => { + const value = 'User: ${user.name}, Age: ${user.age}, Score: ${user.score}'; + const data = { + user: { + name: 'Alice', + age: 25, + score: 95, + }, + }; + const result = template(value, data); + expect(result).toBe('User: Alice, Age: 25, Score: 95'); + }); + + // 测试7: 数据源为 undefined 或 null + test('should handle undefined or null data source', () => { + const value = 'Test ${some.value}'; + + // 测试 undefined + const result1 = template(value, undefined); + expect(result1).toBe(value); + + // 测试 null + const result2 = template(value, null as any); + expect(result2).toBe(value); + }); + + // 测试8: 空字符串输入 + test('should handle empty string input', () => { + const value = ''; + const result = template(value); + expect(result).toBe(''); + }); + + // 测试9: 正则表达式匹配测试 + test('should correctly match template patterns', () => { + // 测试正则表达式是否能正确匹配模板变量 + const testString = '${a.b.c} ${x.y.z}'; + const matches = testString.match(dataSourceTemplateRegExp); + expect(matches).not.toBeNull(); + expect(matches?.length).toBeGreaterThan(0); + }); + + // 测试10: 复杂混合情况 + test('should handle complex mixed cases', () => { + const value = 'User: ${user.name}, ${user.age} years old. ${not.found} ${user.address.city}'; + const data = { + user: { + name: 'Bob', + age: 40, + address: { + city: 'Shanghai', + }, + }, + }; + const result = template(value, data); + expect(result).toBe('User: Bob, 40 years old. ${not.found} Shanghai'); + }); }); describe('createIteratorContentData', () => { diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 64773db3..19a587f1 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -296,7 +296,7 @@ export const guid = (digit = 8): string => export const getKeysArray = (keys: string | number) => // 将 array[0] 转成 array.0 - `${keys}`.replaceAll(/\[(\d+)\]/g, '.$1').split('.'); + `${keys}`.replace(/\[(\d+)\]/g, '.$1').split('.'); export const getValueByKeyPath = ( keys: number | string | string[] = '', diff --git a/packages/utils/tests/unit/index.spec.ts b/packages/utils/tests/unit/index.spec.ts index 916a287c..fb8cd344 100644 --- a/packages/utils/tests/unit/index.spec.ts +++ b/packages/utils/tests/unit/index.spec.ts @@ -697,3 +697,69 @@ describe('compiledCond', () => { expect(util.compiledCond('is', NaN, undefined)).toBeFalsy(); }); }); + +describe('getKeysArray', () => { + // 测试普通数组索引转换 + test('应该将array[0]转换为["array", "0"]', () => { + const input = 'array[0]'; + const expected = ['array', '0']; + const result = util.getKeysArray(input); + expect(result).toEqual(expected); + }); + + // 测试多层数组索引转换 + test('应该将array[0][1]转换为["array", "0", "1"]', () => { + const input = 'array[0][1]'; + const expected = ['array', '0', '1']; + const result = util.getKeysArray(input); + expect(result).toEqual(expected); + }); + + // 测试数字输入 + test('应该将数字123转换为["123"]', () => { + const input = 123; + const expected = ['123']; + const result = util.getKeysArray(input); + expect(result).toEqual(expected); + }); + + // 测试不含数组索引的字符串 + test('应该将普通字符串按点号分割', () => { + const input = 'a.b.c'; + const expected = ['a', 'b', 'c']; + const result = util.getKeysArray(input); + expect(result).toEqual(expected); + }); + + // 测试空字符串 + test('应该正确处理空字符串', () => { + const input = ''; + const expected = ['']; + const result = util.getKeysArray(input); + expect(result).toEqual(expected); + }); + + // 测试混合格式 + test('应该正确处理混合格式的字符串', () => { + const input = 'obj.array[0].prop[1]'; + const expected = ['obj', 'array', '0', 'prop', '1']; + const result = util.getKeysArray(input); + expect(result).toEqual(expected); + }); + + // 测试特殊字符情况 + test('应该正确处理包含特殊字符的字符串', () => { + const input = 'obj.array[0].prop-with-dash[1]'; + const expected = ['obj', 'array', '0', 'prop-with-dash', '1']; + const result = util.getKeysArray(input); + expect(result).toEqual(expected); + }); + + // 测试只有数组索引的情况 + test('应该正确处理只有数组索引的情况', () => { + const input = '[0][1]'; + const expected = ['', '0', '1']; + const result = util.getKeysArray(input); + expect(result).toEqual(expected); + }); +});