import { readFileSync } from 'fs';
import { CSharpStringExtractor, CodeSnippet } from '../src/CSharpStringExtractor';
describe('CSharpStringExtractor', () => {
let extractor: CSharpStringExtractor;
beforeEach(() => {
extractor = new CSharpStringExtractor();
});
// 测试从普通函数调用参数中提取字符串表达式信息, 只需要提取参数中的字符串值表达式, 不需要对提取的字符串表达式进行处理
test('should extract plain strings', () => {
const code = 'Console.WriteLine("Hello, world!");';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].literals).toEqual(['Hello, world!']);
});
// 已经追加 `.TR()` 后缀的字符串表达式不需要重复追加 `.TR()`
test('should handle strings with .TR() already appended', () => {
const code = 'obj.prop = "Hello, world!".TR();';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('"Hello, world!".TR()');
expect(snippets[0].convertedCode).toBe('"Hello, world!".TR()');
expect(snippets[0].isChanged).toBe(false);
});
// 测试从普通函数调用参数中提取字符串表达式信息, 只需要提取参数中的字符串值表达式, 不需要对提取的字符串表达式进行处理, 也不需要追加 `.TR()`
test('should not add .TR() to function arguments', () => {
const code = 'CallFunc("Hello, world!");';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('"Hello, world!"');
expect(snippets[0].convertedCode).toBe('"Hello, world!"');
});
// 测试从普通属性赋值语句中提取字符串表达式信息, 只需要提取赋值表达式中的字符串值表达式, 不需要对提取的字符串表达式进行处理, 也不需要追加 `.TR()`
test('should not add .TR() to regular property assignments', () => {
const code = 'obj.prop = "Hello, world!";';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('"Hello, world!"');
expect(snippets[0].convertedCode).toBe('"Hello, world!"');
});
// 测试从 $"" 字符串模板(也叫内插字符串)中提取字符串表达式信息, 需要先将字符串模板转换为 `string.Format(...)` 格式, 然后将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息
test('should handle $"" string templates', () => {
const code = 'var label1 = $"Hello, {name}!";';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('$"Hello, {name}!"');
expect(snippets[0].literals).toContain('Hello, {0}!');
expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
});
// 测试从普通函数调用参数中提取 $"" 字符串模板(也叫内插字符串), 需要先将字符串模板转换为 `string.Format(...)` 格式, 然后将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息
test('should handle $"" string templates in function calls', () => {
const code = 'CallFunc($"Hello, world: {obj.Func1()}");';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('$"Hello, world: {obj.Func1()}"');
expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, world: {0}", obj.Func1())');
});
// 测试遇到 `string.Format(...)` 形式的字符串表达式, 需要先将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息, 内插字符串概念参考: `https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/tokens/interpolated`
test('should convert string.Format to Tr.Format', () => {
const code = 'string.Format("Hello, ", name, "!");';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
// string.Format(...) 形式的字符串表达式, 不需要对提取的字符串表达式进行处理, 也不需要追加 `.TR()`
// string.Format(...) 形式包含的字符串表达式, 需要特殊处理, 连带 `string.Format()` 一起捕获存入 originalCode 成员
expect(snippets[0].originalCode).toBe('string.Format("Hello, ", name, "!")');
expect(snippets[0].convertedCode).toBe('Tr.Format("Hello, ", name, "!")');
});
// 测试遇到 `string.Format(...)` 形式的字符串表达式, 需要先将 `string.Format(...)` 替换为 `Tr.Format(...)`, 之后从 `Tr.Format(...)` 的函数调用参数中提取普通字符串、或进一步递归提取字符串表达式信息
// 同时测试用 `+` 连接的字符串表达式, 确保用 `+` 符号拼接的字符串表达式被整体处理, 从中提取字符串表达式信息, 而不是分别处理 `+` 两边每个字符串表达式
test('should handle string.Format in .text assignments', () => {
const code = 'label.text = Tr.Format("pre", Func(), "sub") + other;';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('Tr.Format("pre", Func(), "sub") + other');
expect(snippets[0].literals).toContain('pre');
expect(snippets[0].literals).toContain('sub');
});
// 形如 `m_btn_xxx.title = yyy;` 的赋值语句要和形如 `xxx.text = yyy;` 的赋值语句完全等同处理, 其中 `xxx`和`yyy` 代表某段C#字面量, 可以是对象、字符串、或其他表达式, `m_btn_` 为引用符号名固定前缀; 为了便于理解, 用正则表达式表示, 也就是相当于需要同时匹配 `/^m_btn_\w+\.title\s*=/` 形式的表达式和 `/\w+\.text\s*=/` 形式的表达式, 而其中对于 `xxx` 和 `yyy` 部分的处理逻辑是完全一致的
test('should handle m_btn_xxx.title = yyy', () => {
const code = `
buy.title = "lkwjelkfj1";
m_btn1_buy.title = "lkwjelkfj2";
am_btn_buy.title = "lkwjelkfj3";
m_btn_buy.title = "lkwjelkfj4";
m_btn_wwefHwref.title = $"Hello";
m_btn_fxx_wf.title = $"Hello, {name}!";
`;
const snippets = extractor.extractStrings(code);
{
let snippet = snippets[0];
expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj1"'));
expect(snippet.originalCode).toBe('"lkwjelkfj1"');
// `buy` 不符合 `m_btn_` 开头的条件, 不需要强制加 `.TR()`
expect(snippet.convertedCode).toBe('"lkwjelkfj1"');
expect(snippet.literals).toContain('lkwjelkfj1');
expect(snippet.isChanged).toBe(false);
}
{
let snippet = snippets[1];
expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj2"'));
expect(snippet.originalCode).toBe('"lkwjelkfj2"');
// `m_btn1_buy` 不符合 `m_btn_` 开头的条件, 不需要强制加 `.TR()`
expect(snippet.convertedCode).toBe('"lkwjelkfj2"');
expect(snippet.literals).toContain('lkwjelkfj2');
expect(snippet.isChanged).toBe(false);
}
{
let snippet = snippets[2];
expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj3"'));
expect(snippet.originalCode).toBe('"lkwjelkfj3"');
// `am_btn_buy` 不符合 `m_btn_` 开头的条件, 不需要强制加 `.TR()`
expect(snippet.convertedCode).toBe('"lkwjelkfj3"');
expect(snippet.literals).toContain('lkwjelkfj3');
expect(snippet.isChanged).toBe(false);
}
{
let snippet = snippets[3];
expect(snippet.originalIndex).toBe(code.indexOf('"lkwjelkfj4"'));
expect(snippet.originalCode).toBe('"lkwjelkfj4"');
// `m_btn_buy` 符合 `m_btn_` 开头的条件, 需要强制加 `.TR()`
expect(snippet.convertedCode).toBe('"lkwjelkfj4".TR()');
expect(snippet.literals).toContain('lkwjelkfj4');
expect(snippet.isChanged).toBe(true);
}
{
let snippet = snippets[4];
expect(snippet.originalIndex).toBe(code.indexOf('$"Hello"'));
expect(snippet.originalCode).toBe('$"Hello"');
// `m_btn_wwefHwref` 符合 `m_btn_` 开头的条件, 需要强制加 `.TR()`
expect(snippet.convertedCode).toBe('$"Hello".TR()');
expect(snippet.literals).toContain('Hello');
expect(snippet.isChanged).toBe(true);
}
{
let snippet = snippets[5];
expect(snippet.originalIndex).toBe(code.indexOf('$"Hello, {name}!"'));
expect(snippet.originalCode).toBe('$"Hello, {name}!"');
// `m_btn_fxx_wf` 符合 `m_btn_` 开头的条件
expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
expect(snippet.literals).toContain('Hello, {0}!');
expect(snippet.isChanged).toBe(true);
}
expect(snippets.length).toBe(6);
});
// 测试用 `+` 连接的字符串表达式, 确保用 `+` 符号拼接的字符串表达式被整体处理, 从中提取字符串表达式信息, 而不是分别处理 `+` 两边每个字符串表达式; 并且给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式
test('should handle string concatenation', () => {
const code = 'var label2 = "Hello, " + name + "!";';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('"Hello, " + name + "!"');
expect(snippets[0].convertedCode).toBe('"Hello, ".TR() + name.TR() + "!".TR()');
});
// 测试 `xxx.text = yyy;` 形式的赋值语句, 确保将 `yyy` 中的字符串表达式信息提取出来, 并追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式; 其中 `xxx`和`yyy` 代表某段C#字面量, 可以是对象、字符串、或其他表达式
test('should handle .text = assignments with function calls', () => {
const code = 'label.text = Func();';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('Func()');
expect(snippets[0].convertedCode).toBe('Func().TR()');
});
// 测试 `xxx.text = xxx + xxx;` 形式的赋值语句, 确保将 `xxx.text` 中的 `xxx` 字符串表达式提取出来, 并追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式; 其中 `xxx`和`yyy` 代表某段C#表达式, 可以是对象、字符串、或其他表达式
test('should handle .text = assignments with multiple function calls', () => {
const code = 'label.text = Func() + Func();';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('Func() + Func()');
expect(snippets[0].convertedCode).toBe('Func().TR() + Func().TR()');
});
// 测试 `xxx.text = yyy;` 形式的赋值语句, 确保将 `yyy` 中的 `xxx` 内插字符串提取出来, 并转换为 `Tr.Format(...)` 形式; 其中 `xxx`和`yyy` 代表某段C#表达式, 可以是对象、字符串、或其他表达式
test('should handle .text = assignments with string templates', () => {
const code = 'label.text = $"pre{Func()}sub";';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('$"pre{Func()}sub"');
expect(snippets[0].literals).toContain('pre{0}sub');
expect(snippets[0].convertedCode).toBe('Tr.Format("pre{0}sub", Func())');
});
// 测试从内插字符串中提取字符串表达式信息时, 需要注意转换和捕获内插字符串格式参数, 内插字符串和对应格式参数参考: `https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/tokens/interpolated`
test('should handle .text = assignments with string.Format', () => {
const code = 'label.text = string.Format("pre{0:F2}{1}", Func(), "sub") + other;';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('string.Format("pre{0:F2}{1}", Func(), "sub") + other');
expect(snippets[0].literals).toContain('pre{0:F2}{1}');
expect(snippets[0].literals).toContain('sub');
});
// 测试用 `+` 连接的字符串表达式, 确保用 `+` 符号拼接的字符串表达式被整体处理, 从中提取字符串表达式信息, 而不是分别处理 `+` 两边每个字符串表达式; 并且给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式或进一步递归处理
test('should handle complex concatenation with existing .TR() calls', () => {
const code = 'var hello3 = "Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop + "!";';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('"Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop + "!"');
expect(snippets[0].convertedCode).toBe('"Hello, ".TR() + name.TR() + obj2.name.TR() + obj2.nam1_e.Fn1().Prop.TR() + "!".TR()');
});
// 测试处理包含转义引号的字符串表达式, 确保能正确提取和转换转义引号, 而不会影响字符串边界的正常解析
test('should handle escaped quotes in strings', () => {
const code = 'Debug.Log("aaa\\\"bbb\\\"c\\\"d\\\\\\"cc");';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('"aaa\\\"bbb\\\"c\\\"d\\\\\\"cc"');
expect(snippets[0].convertedCode).toBe('"aaa\\\"bbb\\\"c\\\"d\\\\\\"cc"');
// 由于JavaScript字符串转义的复杂性,我们只检查是否提取了字符串
expect(snippets[0].literals).toEqual(['aaa\\\"bbb\\\"c\\\"d\\\\\\"cc']);
});
// 测试处理包含 `+` 连接的字符串表达式, 需要给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式或进一步递归处理
test('should capture statement boundaries correctly', () => {
const code = 'obj.text = "Test property" + "Test property2";';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(1);
expect(snippets[0].originalCode).toBe('"Test property" + "Test property2"');
expect(snippets[0].convertedCode).toBe('"Test property".TR() + "Test property2".TR()');
});
// 测试处理包含多个语句的C#代码, 确保能从各个语句中正确提取和转换每个语句中的字符串表达式, 而不会影响其他语句的正常解析
test('should handle multiple statements', () => {
const code = 'obj1.text = "Hello"; obj2.text = "World";';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(2);
expect(snippets[0].originalCode).toBe('"Hello"');
expect(snippets[0].convertedCode).toBe('"Hello".TR()');
expect(snippets[1].originalCode).toBe('"World"');
expect(snippets[1].convertedCode).toBe('"World".TR()');
});
// 测试处理作为函数参数的内插字符串表达式, 确保能从内插字符串中提取字符串表达式信息, 并转换为 `Tr.Format(...)` 形式
test('should extract and convert string templates', () => {
const code = 'Ljk.Ilk($"Hello, {name}!");';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBeGreaterThan(0);
const snippet = snippets[0];
expect(snippet.originalCode).toBe('$"Hello, {name}!"');
expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
expect(snippet.literals).toEqual(['Hello, {0}!']);
expect(snippet.isChanged).toBe(true);
});
// 测试处理 `string.Format` 调用, 确保能将其转换为 `Tr.Format(...)` 形式, 并正确提取字符串表达式信息
test('should convert string.Format to Tr.Format', () => {
const code = 'string.Format("Hello, {0}!", name);';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBeGreaterThan(0);
const snippet = snippets[0];
expect(snippet.originalCode).toBe('string.Format("Hello, {0}!", name)');
expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
expect(snippet.literals).toEqual(['Hello, {0}!']);
expect(snippet.isChanged).toBe(true);
});
// 测试处理 `xxx.text = yyy;` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式, 需要从 `yyy` 中正确提取字符串表达式信息, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式
test('should handle .text = assignments', () => {
const code = 'label.text = "Test";';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBeGreaterThan(0);
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"Test"');
expect(snippet.convertedCode).toBe('"Test".TR()');
expect(snippet.literals).toEqual(['Test']);
expect(snippet.isChanged).toBe(true);
});
// 测试处理包含 `+` 连接的字符串表达式, 需要给 `+` 连接的每个成分追加 `.TR()` 或者转换为 `Tr.Format(...)` 形式或进一步递归处理
test('should handle string concatenations', () => {
const code = 'var wkleee = "Hello, " + name + "!";';
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBeGreaterThan(0);
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"Hello, " + name + "!"');
expect(snippet.convertedCode).toBe('"Hello, ".TR() + name.TR() + "!".TR()');
expect(snippet.literals).toEqual(['Hello, ', '!']);
expect(snippet.isChanged).toBe(true);
});
// 测试处理 `+` 连接的字符串表达式时, 确保不会给已经包含 `.TR()` 的字符串表达式再次追加 `.TR()`
test('should not add .TR() to strings already having it', () => {
const code = 'var wkle = "Hello".TR();';
const snippets = extractor.extractStrings(code);
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"Hello".TR()');
expect(snippet.convertedCode).toBe('"Hello".TR()');
expect(snippet.literals).toEqual(['Hello']);
expect(snippet.isChanged).toBe(false);
});
// 测试处理复杂的 `.text =` 赋值语句, 确保能正确处理包含 `string.Format` 调用和 `+` 连接的字符串表达式, 并转换为 `Tr.Format(...)` 形式或进一步递归处理
test('should handle complex text assignments', () => {
const code = 'label.text = string.Format("pre{0}sub", Func()) + other;';
const snippets = extractor.extractStrings(code);
const snippet = snippets[0];
expect(snippet.originalCode).toBe('string.Format("pre{0}sub", Func()) + other');
expect(snippet.convertedCode).toBe('Tr.Format("pre{0}sub", Func()) + other.TR()');
expect(snippet.literals).toEqual(['pre{0}sub']);
expect(snippet.isChanged).toBe(true);
});
// 测试处理转义字符串, 确保能正确处理包含转义字符的字符串表达式, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式
test('should handle escaped strings', () => {
const code = 'a.text = "aaa\\"bbb\\"c\\"d\\\\\\"cc";';
const snippets = extractor.extractStrings(code);
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"aaa\\"bbb\\"c\\"d\\\\\\"cc"');
expect(snippet.convertedCode).toBe('"aaa\\"bbb\\"c\\"d\\\\\\"cc".TR()');
expect(snippet.literals).toEqual(['aaa\\"bbb\\"c\\"d\\\\\\"cc']);
expect(snippet.isChanged).toBe(true);
});
// 测试处理 `@$""` 和 `$@""` 格式的字符串表达式, 确保能将其转换为 `Tr.Format(...)` 形式, 并正确提取字符串表达式信息
test('should handle @$"" and $@"" formats', () => {
const code = 'Fcx.Kjl(@$"Hello, {name}!");';
const snippets = extractor.extractStrings(code);
const snippet = snippets[0];
expect(snippet.originalCode).toBe('@$"Hello, {name}!"');
expect(snippet.convertedCode).toBe('Tr.Format("Hello, {0}!", name)');
expect(snippet.literals).toEqual(['Hello, {0}!']);
expect(snippet.isChanged).toBe(true);
});
// 测试处理函数调用时, 确保能正确提取参数中的字符串表达式, 不需要给字符串表达式追加 `.TR()` 或 转换为 `Tr.Format(...)` 形式, 除非遇到由 `+` 连接的字符串表达式
test('should handle function calls with string arguments', () => {
const code = 'CallFunc("Hello", "World");';
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"Hello"');
expect(snippet.convertedCode).toBe('"Hello"');
expect(snippet.literals).toEqual(['Hello']);
expect(snippet.isChanged).toBe(false);
}
{
const snippet = snippets[1];
expect(snippet.originalCode).toBe('"World"');
expect(snippet.convertedCode).toBe('"World"');
expect(snippet.literals).toEqual(['World']);
expect(snippet.isChanged).toBe(false);
}
});
// 测试处理 `.text =` 赋值语句时, 确保能正确处理包含 `Tr.Format(...)` 调用的字符串表达式, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式
test('should handle .text = with Tr.Format', () => {
const code = 'label.text = Tr.Format("pre", Func(), "sub") + other;';
const snippets = extractor.extractStrings(code);
const snippet = snippets[0];
expect(snippet.originalCode).toBe('Tr.Format("pre", Func(), "sub") + other');
expect(snippet.convertedCode).toBe('Tr.Format("pre", Func(), "sub") + other.TR()');
expect(snippet.literals).toEqual(['pre', 'sub']);
expect(snippet.isChanged).toBe(true);
});
// 测试处理 `.text =` 赋值语句时, 确保能正确处理包含 `Tr.Format(...)` 调用的字符串表达式, 并给普通字符串表达式追加 `.TR()` 或者让内插字符串转换为 `Tr.Format(...)` 形式, 除非遇到由 `+` 连接的字符串表达式
test('should handle .text = with Tr.Format 2', () => {
const code = 'm_text_lastAward.text = $"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]";';
const snippets = extractor.extractStrings(code);
const snippet = snippets[0];
expect(snippet.originalCode).toBe('$"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]"');
expect(snippet.convertedCode).toBe('$"[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]".TR()');
expect(snippet.literals).toEqual(['[color=#FFFFFF]上期奖励:[/color][color=#FFE97E][/color][color=#FFFFFF]暂无获奖[/color]']);
expect(snippet.isChanged).toBe(true);
});
// 处理C#多行语句
test('should handle non string assignment 1', () => {
const code = 'var d1 = 12;var d2 = 13;var d3 = d1 + d2;var aa = "aaa";';
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"aaa"');
expect(snippet.convertedCode).toBe('"aaa"');
expect(snippet.literals).toEqual(['aaa']);
expect(snippet.isChanged).toBe(false);
}
});
// 处理C#多行语句
test('should handle non string assignment 2', () => {
const code = 'var d3 = d1 + d2;var aa = "aaa";';
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"aaa"');
expect(snippet.convertedCode).toBe('"aaa"');
expect(snippet.literals).toEqual(['aaa']);
expect(snippet.isChanged).toBe(false);
}
});
// 处理C#多行语句
test('should handle multilne content 1', () => {
const code = 'var aa = "aaa";\nvar bb = "bbb";\nvar cc = "ccc";';
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"aaa"');
expect(snippet.convertedCode).toBe('"aaa"');
expect(snippet.literals).toEqual(['aaa']);
expect(snippet.isChanged).toBe(false);
}
{
const snippet = snippets[1];
expect(snippet.originalCode).toBe('"bbb"');
expect(snippet.convertedCode).toBe('"bbb"');
expect(snippet.literals).toEqual(['bbb']);
expect(snippet.isChanged).toBe(false);
}
{
const snippet = snippets[2];
expect(snippet.originalCode).toBe('"ccc"');
expect(snippet.convertedCode).toBe('"ccc"');
expect(snippet.literals).toEqual(['ccc']);
expect(snippet.isChanged).toBe(false);
}
});
// 处理C#多行语句
test('should handle multilne content 2', () => {
const code = 'var d1 = 12;var d2 = 13;var d3 = d1 + d2;var aa = "aaa";\nvar bb = "bbb";\nvar dd = aa + bb;\nvar hh = aa + bb + "hhh";\ncc.text = "ccc" + aa + bb;var ii = "iii";jj.text = "jjj";CallFunc("jjj");CallFunc2("jjj", "kkk");jj.text = "jjj" + ll;';
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"aaa"');
expect(snippet.convertedCode).toBe('"aaa"');
expect(snippet.literals).toEqual(['aaa']);
expect(snippet.isChanged).toBe(false);
}
{
const snippet = snippets[1];
expect(snippet.originalCode).toBe('"bbb"');
expect(snippet.convertedCode).toBe('"bbb"');
expect(snippet.literals).toEqual(['bbb']);
expect(snippet.isChanged).toBe(false);
}
{
const snippet = snippets[2];
expect(snippet.originalCode).toBe('aa + bb + "hhh"');
expect(snippet.convertedCode).toBe('aa.TR() + bb.TR() + "hhh".TR()');
expect(snippet.literals).toEqual(['hhh']);
expect(snippet.isChanged).toBe(true);
}
{
const snippet = snippets[3];
expect(snippet.originalCode).toBe('"ccc" + aa + bb');
expect(snippet.convertedCode).toBe('"ccc".TR() + aa.TR() + bb.TR()');
expect(snippet.literals).toEqual(['ccc']);
expect(snippet.isChanged).toBe(true);
}
{
const snippet = snippets[4];
expect(snippet.originalCode).toBe('"iii"');
expect(snippet.convertedCode).toBe('"iii"');
expect(snippet.literals).toEqual(['iii']);
expect(snippet.isChanged).toBe(false);
}
{
const snippet = snippets[5];
expect(snippet.originalCode).toBe('"jjj"');
expect(snippet.convertedCode).toBe('"jjj".TR()');
expect(snippet.literals).toEqual(['jjj']);
expect(snippet.isChanged).toBe(true);
}
{
const snippet = snippets[6];
expect(snippet.originalCode).toBe('"jjj"');
expect(snippet.convertedCode).toBe('"jjj"');
expect(snippet.literals).toEqual(['jjj']);
expect(snippet.isChanged).toBe(false);
}
{
const snippet = snippets[7];
expect(snippet.originalCode).toBe('"jjj"');
expect(snippet.convertedCode).toBe('"jjj"');
expect(snippet.literals).toEqual(['jjj']);
expect(snippet.isChanged).toBe(false);
}
{
const snippet = snippets[8];
expect(snippet.originalCode).toBe('"kkk"');
expect(snippet.convertedCode).toBe('"kkk"');
expect(snippet.literals).toEqual(['kkk']);
expect(snippet.isChanged).toBe(false);
}
{
const snippet = snippets[9];
expect(snippet.originalCode).toBe('"jjj" + ll');
expect(snippet.convertedCode).toBe('"jjj".TR() + ll.TR()');
expect(snippet.literals).toEqual(['jjj']);
expect(snippet.isChanged).toBe(true);
}
});
// 处理C#多行语句
test('should handle multilne content 3', () => {
const code = 'label.text = Tr.Format("pre", Func(), "sub") + other;\nlabel2.text = Tr.Format("pre2", Func(), "sub2") + other2;';
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('Tr.Format("pre", Func(), "sub") + other');
expect(snippet.convertedCode).toBe('Tr.Format("pre", Func(), "sub") + other.TR()');
expect(snippet.literals).toEqual(['pre', 'sub']);
expect(snippet.isChanged).toBe(true);
}
{
const snippet = snippets[1];
expect(snippet.originalCode).toBe('Tr.Format("pre2", Func(), "sub2") + other2');
expect(snippet.convertedCode).toBe('Tr.Format("pre2", Func(), "sub2") + other2.TR()');
expect(snippet.literals).toEqual(['pre2', 'sub2']);
expect(snippet.isChanged).toBe(true);
}
});
// 处理C#多行语句
test('should handle multilne content 4', () => {
const code = 'label.text =\n Tr.Format(\n\t"pre", Func(),\n\t\t "sub") + other;';
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('Tr.Format(\n\t"pre", Func(),\n\t\t "sub") + other');
expect(snippet.convertedCode).toBe('Tr.Format(\n\t"pre", Func(),\n\t\t "sub") + other.TR()');
expect(snippet.literals).toEqual(['pre', 'sub']);
expect(snippet.isChanged).toBe(true);
}
});
// 处理C#多行语句
test('should handle multilne content 5', () => {
const code = `
// 注释中无字符串表达式时, 不需要包含在捕获部分里
m_text_name.text = cardTable.Name;
`;
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('cardTable.Name');
expect(snippet.convertedCode).toBe('cardTable.Name.TR()');
expect(snippet.literals).toEqual([]);
expect(snippet.isChanged).toBe(true);
}
});
// 处理C#多行语句
test('should handle multilne content 6', () => {
const code = `
if (condition)
{
Toast.Info("卸载成功");
}
`;
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"卸载成功"');
expect(snippet.convertedCode).toBe('"卸载成功"');
expect(snippet.literals).toEqual(['卸载成功']);
expect(snippet.originalIndex).toBe(code.indexOf('"卸载成功"'));
expect(snippet.isChanged).toBe(false);
}
});
// 处理C#多行语句
test('should handle multilne content 7', () => {
const code = `
while (condition)
{
TT1.Fn1("卸载成功");
}
`;
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"卸载成功"');
expect(snippet.convertedCode).toBe('"卸载成功"');
expect(snippet.literals).toEqual(['卸载成功']);
expect(snippet.originalIndex).toBe(code.indexOf('"卸载成功"'));
expect(snippet.isChanged).toBe(false);
}
});
// 处理C#多行语句
test('should handle multilne content 8', () => {
const code = `
for (scope;condition;continuex)
{
QR2.Fn2("卸载成功");
}
`;
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"卸载成功"');
expect(snippet.convertedCode).toBe('"卸载成功"');
expect(snippet.literals).toEqual(['卸载成功']);
expect(snippet.isChanged).toBe(false);
}
});
// 处理C#多行语句
test('should handle multilne content 9', () => {
const code = `
{
KK3.Ca3("卸载成功");
}
`;
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"卸载成功"');
expect(snippet.convertedCode).toBe('"卸载成功"');
expect(snippet.literals).toEqual(['卸载成功']);
expect(snippet.isChanged).toBe(false);
}
});
// 处理C#多行语句
test('should handle multilne content 10', () => {
const code = `
var lambda1 = () => {
LJN4.Fn4("卸载成功");
};
`;
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"卸载成功"');
expect(snippet.convertedCode).toBe('"卸载成功"');
expect(snippet.literals).toEqual(['卸载成功']);
expect(snippet.isChanged).toBe(false);
}
});
// 处理C#多行语句
test('should handle multilne content 11', () => {
const code = `
void lambda1() {
KK5.Call5("卸载成功");
}
`;
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('"卸载成功"');
expect(snippet.convertedCode).toBe('"卸载成功"');
expect(snippet.literals).toEqual(['卸载成功']);
expect(snippet.isChanged).toBe(false);
}
});
// 处理C#多行语句
test('should handle multilne content 12', () => {
const code = `
//星级
m_list_star.RemoveChildrenToPool();
for (int i = 0; i < _sutraCardData.LevelStar; i++)
{
var item = m_list_star.AddItemFromPool() as ItemSutraStarSComp;
if (item == null) continue;
item.SetSutraStar(_sutraCardData.Level);
}
m_text_quality.text = Tr.Format("{0}阶", _sutraCardData.Quality);
//设置通玄值
m_text_tongXuan.text = "通玄:".TR() + (_sutraCardData.InheritAtkPercent / 100).ToString("0.0") + "%".TR();
`;
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalCode).toBe('Tr.Format("{0}阶", _sutraCardData.Quality)');
expect(snippet.convertedCode).toBe('Tr.Format("{0}阶", _sutraCardData.Quality)');
expect(snippet.literals).toEqual(['{0}阶']);
expect(snippet.isChanged).toBe(false);
}
});
// 处理C#多行语句
test('should handle multilne content 13', () => {
const code = `
Toast.Info("卸载成功");
Toast.Info("卸载成功");
`;
const snippets = extractor.extractStrings(code);
{
const snippet = snippets[0];
expect(snippet.originalIndex).toBe(code.indexOf('"卸载成功"'));
expect(snippet.originalCode).toBe('"卸载成功"');
expect(snippet.convertedCode).toBe('"卸载成功"');
expect(snippet.literals).toEqual(['卸载成功']);
expect(snippet.isChanged).toBe(false);
}
{
const snippet = snippets[1];
expect(snippet.originalIndex).toBe(code.indexOf('"卸载成功"', snippets[0].originalIndex + 1));
expect(snippet.originalCode).toBe('"卸载成功"');
expect(snippet.convertedCode).toBe('"卸载成功"');
expect(snippet.literals).toEqual(['卸载成功']);
expect(snippet.isChanged).toBe(false);
}
});
// 处理C#多行语句
test('should handle multilne content 14', () => {
const code = readFileSync('./test/MainSutraDetailDialog.cs', 'utf8');
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
// 针对每个snippet展开对每个属性值的断言
// Snippet 1
expect(snippets[0].originalIndex).toBe(code.indexOf('cardTable.Name.TR()'));
expect(snippets[0].originalCode).toBe('cardTable.Name.TR()');
expect(snippets[0].convertedCode).toBe('cardTable.Name.TR()');
expect(snippets[0].literals).toEqual([]);
expect(snippets[0].isChanged).toBe(false);
// Snippet 2
expect(snippets[1].originalIndex).toBe(code.indexOf('sutraConfig.Poetry[0].TR()'));
expect(snippets[1].originalCode).toBe('sutraConfig.Poetry[0].TR()');
expect(snippets[1].convertedCode).toBe('sutraConfig.Poetry[0].TR()');
expect(snippets[1].literals).toEqual([]);
expect(snippets[1].isChanged).toBe(false);
// Snippet 3
expect(snippets[2].originalIndex).toBe(code.indexOf('sutraConfig.Poetry[1].TR()'));
expect(snippets[2].originalCode).toBe('sutraConfig.Poetry[1].TR()');
expect(snippets[2].convertedCode).toBe('sutraConfig.Poetry[1].TR()');
expect(snippets[2].literals).toEqual([]);
expect(snippets[2].isChanged).toBe(false);
// Snippet 4
expect(snippets[3].originalIndex).toBe(code.indexOf('(_sutraCardData.AttackUp() / 100).ToString("0.0") + "%".TR()'));
expect(snippets[3].originalCode).toBe('(_sutraCardData.AttackUp() / 100).ToString("0.0") + "%".TR()');
expect(snippets[3].convertedCode).toBe('(_sutraCardData.AttackUp() / 100).ToString("0.0").TR() + "%".TR()');
expect(snippets[3].literals).toEqual(['0.0', '%']);
expect(snippets[3].isChanged).toBe(true);
// Snippet 5
expect(snippets[4].originalIndex).toBe(code.indexOf('(_sutraCardData.HpUp() / 100).ToString("0.0") + "%".TR()'));
expect(snippets[4].originalCode).toBe('(_sutraCardData.HpUp() / 100).ToString("0.0") + "%".TR()');
expect(snippets[4].convertedCode).toBe('(_sutraCardData.HpUp() / 100).ToString("0.0").TR() + "%".TR()');
expect(snippets[4].literals).toEqual(['0.0', '%']);
expect(snippets[4].isChanged).toBe(true);
// Snippet 6
expect(snippets[5].originalIndex).toBe(code.indexOf('skillConfig.Name.TR()'));
expect(snippets[5].originalCode).toBe('skillConfig.Name.TR()');
expect(snippets[5].convertedCode).toBe('skillConfig.Name.TR()');
expect(snippets[5].literals).toEqual([]);
expect(snippets[5].isChanged).toBe(false);
// Snippet 7
expect(snippets[6].originalIndex).toBe(code.indexOf('passiveSkillConfig != null ? passiveSkillConfig.Description.TR() : "被动技能未解锁".TR()'));
expect(snippets[6].originalCode).toBe('passiveSkillConfig != null ? passiveSkillConfig.Description.TR() : "被动技能未解锁".TR()');
expect(snippets[6].convertedCode).toBe('passiveSkillConfig != null ? passiveSkillConfig.Description.TR() : "被动技能未解锁".TR()');
expect(snippets[6].literals).toEqual(['被动技能未解锁']);
expect(snippets[6].isChanged).toBe(false);
// Snippet 8
expect(snippets[7].originalIndex).toBe(code.indexOf('"Effect_FaBao_Unlock"'));
expect(snippets[7].originalCode).toBe('"Effect_FaBao_Unlock"');
expect(snippets[7].convertedCode).toBe('"Effect_FaBao_Unlock"');
expect(snippets[7].literals).toEqual(['Effect_FaBao_Unlock']);
expect(snippets[7].isChanged).toBe(false);
// Snippet 9
expect(snippets[8].originalIndex).toBe(code.indexOf('Tr.Format("{0}阶", _sutraCardData.Quality)'));
expect(snippets[8].originalCode).toBe('Tr.Format("{0}阶", _sutraCardData.Quality)');
expect(snippets[8].convertedCode).toBe('Tr.Format("{0}阶", _sutraCardData.Quality)');
expect(snippets[8].literals).toEqual(['{0}阶']);
expect(snippets[8].isChanged).toBe(false);
// Snippet 10
expect(snippets[9].originalIndex).toBe(code.indexOf('"通玄:".TR() + (_sutraCardData.InheritAtkPercent / 100).ToString("0.0") + "%".TR()'));
expect(snippets[9].originalCode).toBe('"通玄:".TR() + (_sutraCardData.InheritAtkPercent / 100).ToString("0.0") + "%".TR()');
expect(snippets[9].convertedCode).toBe('"通玄:".TR() + (_sutraCardData.InheritAtkPercent / 100).ToString("0.0").TR() + "%".TR()');
expect(snippets[9].literals).toEqual(['通玄:', '0.0', '%']);
expect(snippets[9].isChanged).toBe(true);
// Snippet 11
expect(snippets[10].originalIndex).toBe(code.indexOf(`enough
? Tr.Format("[color=#1B8049]{0}/{1}[/color]", curCount, costCount)
: Tr.Format("[color=#E55E5A]{0}/{1}[/color]", curCount, costCount)`));
expect(snippets[10].originalCode).toBe(`enough
? Tr.Format("[color=#1B8049]{0}/{1}[/color]", curCount, costCount)
: Tr.Format("[color=#E55E5A]{0}/{1}[/color]", curCount, costCount)`);
expect(snippets[10].convertedCode).toBe(`enough
? Tr.Format("[color=#1B8049]{0}/{1}[/color]", curCount, costCount)
: Tr.Format("[color=#E55E5A]{0}/{1}[/color]", curCount, costCount)`);
expect(snippets[10].literals).toEqual(['[color=#1B8049]{0}/{1}[/color]', '[color=#E55E5A]{0}/{1}[/color]']);
expect(snippets[10].isChanged).toBe(false);
// Snippet 12
expect(snippets[11].originalIndex).toBe(code.indexOf('dependLockInfo.Item2.TR()'));
expect(snippets[11].originalCode).toBe('dependLockInfo.Item2.TR()');
expect(snippets[11].convertedCode).toBe('dependLockInfo.Item2.TR()');
expect(snippets[11].literals).toEqual([]);
expect(snippets[11].isChanged).toBe(false);
// Snippet 13
expect(snippets[12].originalIndex).toBe(code.indexOf('"Effect_FaBao_Unlock"', code.indexOf('"Effect_FaBao_Unlock"') + 1));
expect(snippets[12].originalCode).toBe('"Effect_FaBao_Unlock"');
expect(snippets[12].convertedCode).toBe('"Effect_FaBao_Unlock"');
expect(snippets[12].literals).toEqual(['Effect_FaBao_Unlock']);
expect(snippets[12].isChanged).toBe(false);
// Snippet 14
expect(snippets[13].originalIndex).toBe(code.indexOf('Tr.Format("({0}/6)", _sutraCardData.Card.RuneOpenCount)'));
expect(snippets[13].originalCode).toBe('Tr.Format("({0}/6)", _sutraCardData.Card.RuneOpenCount)');
expect(snippets[13].convertedCode).toBe('Tr.Format("({0}/6)", _sutraCardData.Card.RuneOpenCount)');
expect(snippets[13].literals).toEqual(['({0}/6)']);
expect(snippets[13].isChanged).toBe(false);
// Snippet 15
expect(snippets[14].originalIndex).toBe(code.indexOf('$"Can not find tagId in sutra : {_sutraCardData.Card.Id}"'));
expect(snippets[14].originalCode).toBe('$"Can not find tagId in sutra : {_sutraCardData.Card.Id}"');
expect(snippets[14].convertedCode).toBe('Tr.Format("Can not find tagId in sutra : {0}", _sutraCardData.Card.Id)');
expect(snippets[14].literals).toEqual(['Can not find tagId in sutra : {0}']);
expect(snippets[14].isChanged).toBe(true);
//#region 需要捕获类成员赋值表达式中, 出现在赋值操作符`=`右侧的字符串值表达式
// Snippet 16
expect(snippets[15].originalIndex).toBeGreaterThan(0);
expect(snippets[15].originalIndex).toBe(code.indexOf('"提示"'));
expect(snippets[15].originalCode).toBe('"提示"');
expect(snippets[15].convertedCode).toBe('"提示"');
expect(snippets[15].literals).toEqual(['提示']);
expect(snippets[15].isChanged).toBe(false);
// Snippet 17
expect(snippets[16].originalIndex).toBeGreaterThan(0);
expect(snippets[16].originalIndex).toBe(code.indexOf('"是否一键卸下法宝当前镶嵌灵纹"'));
expect(snippets[16].originalCode).toBe('"是否一键卸下法宝当前镶嵌灵纹"');
expect(snippets[16].convertedCode).toBe('"是否一键卸下法宝当前镶嵌灵纹"');
expect(snippets[16].literals).toEqual(['是否一键卸下法宝当前镶嵌灵纹']);
expect(snippets[16].isChanged).toBe(false);
// Snippet 18
expect(snippets[17].originalIndex).toBeGreaterThan(0);
expect(snippets[17].originalIndex).toBe(code.indexOf('"确认"'));
expect(snippets[17].originalCode).toBe('"确认"');
expect(snippets[17].convertedCode).toBe('"确认"');
expect(snippets[17].literals).toEqual(['确认']);
expect(snippets[17].isChanged).toBe(false);
//#endregion 需要捕获类成员赋值句式中, 出现在赋值操作符`=`右侧的字符串值表达式
// Snippet 19
expect(snippets[18].originalIndex).toBeGreaterThan(0);
expect(snippets[18].originalIndex).toBe(code.indexOf('"卸载成功"'));
expect(snippets[18].originalCode).toBe('"卸载成功"');
expect(snippets[18].convertedCode).toBe('"卸载成功"');
expect(snippets[18].literals).toEqual(['卸载成功']);
expect(snippets[18].isChanged).toBe(false);
//#region 需要捕获类成员赋值句式中, 出现在赋值操作符`=`右侧的字符串值表达式
// Snippet 20
expect(snippets[19].originalIndex).toBeGreaterThan(0);
expect(snippets[19].originalIndex).toBe(code.indexOf('"取消"'));
expect(snippets[19].originalCode).toBe('"取消"');
expect(snippets[19].convertedCode).toBe('"取消"');
expect(snippets[19].literals).toEqual(['取消']);
expect(snippets[19].isChanged).toBe(false);
// Snippet 21
expect(snippets[20].originalIndex).toBeGreaterThan(0);
expect(snippets[20].originalIndex).toBe(code.indexOf('"本次登录不再提示"'));
expect(snippets[20].originalCode).toBe('"本次登录不再提示"');
expect(snippets[20].convertedCode).toBe('"本次登录不再提示"');
expect(snippets[20].literals).toEqual(['本次登录不再提示']);
expect(snippets[20].isChanged).toBe(false);
//#endregion 需要捕获类成员赋值句式中, 出现在赋值操作符`=`右侧的字符串值表达式
// Snippet 22
expect(snippets[21].originalIndex).toBeGreaterThan(0);
expect(snippets[21].originalIndex).toBe(code.indexOf('"卸载成功"', code.indexOf('"卸载成功"') + 1));
expect(snippets[21].originalCode).toBe('"卸载成功"');
expect(snippets[21].convertedCode).toBe('"卸载成功"');
expect(snippets[21].literals).toEqual(['卸载成功']);
expect(snippets[21].isChanged).toBe(false);
});
// 处理C#多行语句
test('should handle multilne content 15', () => {
const code = readFileSync('./test/GuildDonateDialog.cs', 'utf8');
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
// 针对每个snippet展开对每个属性值的断言
// 查找包含目标URL的snippet
const targetSnippet = snippets.find(snippet => snippet.originalCode.includes('ui://a0w66rlc7bhfusff'));
expect(targetSnippet).toBeDefined();
if (targetSnippet) {
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"
{paidConfig.Quantity}"`));
expect(targetSnippet.originalCode).toBe(`$"
{paidConfig.Quantity}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("
{0}", paidConfig.Quantity)`);
expect(targetSnippet.literals).toEqual(["
{0}"]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 处理C#普通字符串包含 `//` 的情形
test('should handle string with `//`', () => {
const code = `m_btn_paid.text = "//";`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`"//"`);
expect(targetSnippet.convertedCode).toBe(`"//".TR()`);
expect(targetSnippet.literals).toEqual(['//']);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C# `+=` 形式赋值语句
test('should handle string template with format specifier', () => {
const code = `infoStr += $"最终抗性: {resistReduce:F}\\n";`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`$"最终抗性: {resistReduce:F}\\n"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("最终抗性: {0:F}\\n", resistReduce)`);
expect(targetSnippet.literals).toEqual(['最终抗性: {0:F}\\n']);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C#多行语句, 特别是包含 `\n` 的字符串
test('should handle multilne content 17', () => {
const code = `
infoStr += $"aaa: {aa}\n" +
$"bbb = {bb}";
`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`$"aaa: {aa}\n" +
$"bbb = {bb}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("aaa: {0}\n", aa) +
Tr.Format("bbb = {0}", bb)`);
expect(targetSnippet.literals).toEqual([
'aaa: {0}\n',
'bbb = {0}'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C#多行语句, 特别是包含 `\\n` 的内插字符串
test('should handle multilne content 19', () => {
const code = `
infoStr += $"aaa: {aa}\\n" +
$"bbb = {bb}";
`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`$"aaa: {aa}\\n" +
$"bbb = {bb}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("aaa: {0}\\n", aa) +
Tr.Format("bbb = {0}", bb)`);
expect(targetSnippet.literals).toEqual([
'aaa: {0}\\n',
'bbb = {0}'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C#中多行字符串拼接成的字符串表达式
test('should handle multilne content 18', () => {
const code = `
infoStr += "伤害公式: \\n" +
$"最终伤害[color={colorCode}][{(float)damageValue:F}][/color] = \\n" +
$"基础伤害[b][{baseValue:F}][/b] X \\n" +
$"暴击伤害加成[b][{damageInfo.CritDamageRatio:F}][/b] X \\n" +
$"最终伤害加成[b][{1 + damageInfo.FinalDamageRatio:F}][/b] X \\n" +
$"最终承伤加成[b][{1 + damageInfo.FinalInjuryRatio:F}][/b] / \\n" +
$"最终伤害减免[b][{1 + damageInfo.FinalDamageReduce:F}][/b] X \\n" +
$"抗性减免[b][{resistReduce:F}]";
`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`"伤害公式: \\n" +
$"最终伤害[color={colorCode}][{(float)damageValue:F}][/color] = \\n" +
$"基础伤害[b][{baseValue:F}][/b] X \\n" +
$"暴击伤害加成[b][{damageInfo.CritDamageRatio:F}][/b] X \\n" +
$"最终伤害加成[b][{1 + damageInfo.FinalDamageRatio:F}][/b] X \\n" +
$"最终承伤加成[b][{1 + damageInfo.FinalInjuryRatio:F}][/b] / \\n" +
$"最终伤害减免[b][{1 + damageInfo.FinalDamageReduce:F}][/b] X \\n" +
$"抗性减免[b][{resistReduce:F}]"`);
expect(targetSnippet.convertedCode).toBe(`"伤害公式: \\n".TR() +
Tr.Format("最终伤害[color={0}][{1:F}][/color] = \\n", colorCode, (float)damageValue) +
Tr.Format("基础伤害[b][{0:F}][/b] X \\n", baseValue) +
Tr.Format("暴击伤害加成[b][{0:F}][/b] X \\n", damageInfo.CritDamageRatio) +
Tr.Format("最终伤害加成[b][{0:F}][/b] X \\n", 1 + damageInfo.FinalDamageRatio) +
Tr.Format("最终承伤加成[b][{0:F}][/b] / \\n", 1 + damageInfo.FinalInjuryRatio) +
Tr.Format("最终伤害减免[b][{0:F}][/b] X \\n", 1 + damageInfo.FinalDamageReduce) +
Tr.Format("抗性减免[b][{0:F}]", resistReduce)`);
expect(targetSnippet.literals).toEqual([
'伤害公式: \\n',
'最终伤害[color={0}][{1:F}][/color] = \\n',
'基础伤害[b][{2:F}][/b] X \\n',
'暴击伤害加成[b][{3:F}][/b] X \\n',
'最终伤害加成[b][{4:F}][/b] X \\n',
'最终承伤加成[b][{5:F}][/b] / \\n',
'最终伤害减免[b][{6:F}][/b] X \\n',
'抗性减免[b][{7:F}]'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C#中包含括号的内插字符串
test('should handle bracket 1', () => {
const code = `m_text_time.text = $"下一轮神兽出现倒计时: {Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp)}";`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`$"下一轮神兽出现倒计时: {Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp)}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("下一轮神兽出现倒计时: {0}", Date.GetIntweerpolatedTime(ServerTimer.Instance.Time, _startGameStamp))`);
expect(targetSnippet.literals).toEqual([
'下一轮神兽出现倒计时: {0}'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C#中包含括号的内插字符串
test('should handle bracket 2', () => {
const code = `m_text_time.text = $"神兽离去时间: {Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp)}";`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`$"神兽离去时间: {Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp)}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("神兽离去时间: {0}", Date.FeGetInterpolatedTime(ServerTimer.Instance.Time, _endGameStamp))`);
expect(targetSnippet.literals).toEqual([
'神兽离去时间: {0}'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C#中包含括号的内插字符串
test('should handle bracket 3', () => {
const code = `desc += $"剩余{ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time)}";`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`$"剩余{ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time)}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("剩余{0}", ToGetInterpolatedTime(_cardPoolData.FinishTime * 1000, ServerTimer.Instance.Time))`);
expect(targetSnippet.literals).toEqual([
'剩余{0}'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C#中包含括号的内插字符串
test('should handle bracket 4', () => {
const code = `desc += $"剩余{ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time()))}";`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`$"剩余{ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time()))}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("剩余{0}", ToGetInterpolatedTime(FF(_cardPoolData.FinishTime() * 1000, ServerTimer.Instance.Time())))`);
expect(targetSnippet.literals).toEqual([
'剩余{0}'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C#中包含`+=`符号的内插字符串
test('should handle duplicate tr handle 1', () => {
const code = `infoStr = $"+={colorCode}";`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`$"+={colorCode}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("+={0}", colorCode)`);
expect(targetSnippet.literals).toEqual([
'+={0}'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C# `+=` 形式的赋值语句后接 包含 `=` 符号的内插字符串
test('should handle duplicate tr handle 2', () => {
const code = `infoStr += $"={colorCode}";`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`$"={colorCode}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("={0}", colorCode)`);
expect(targetSnippet.literals).toEqual([
'={0}'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C# `+=` 形式的赋值语句后接 包含 `+=` 符号的内插字符串
test('should handle duplicate tr handle 3', () => {
const code = `infoStr += $"+={colorCode}";`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`$"+={colorCode}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("+={0}", colorCode)`);
expect(targetSnippet.literals).toEqual([
'+={0}'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C# `+=` 形式的赋值语句后接包含 `=`、`[`、`]`、`{`、`}`、`=` 等特殊符号的内插字符串
test('should handle duplicate tr handle 4', () => {
const code = `infoStr += $"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n";`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
expect(targetSnippet.literals).toEqual([
'伤害类型: [color={0}][b]{1}[/b][/color]\n'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 处理C#中函数调用参数中包含普通字符串的情况, 函数参数需要拆分, 逐个参数捕获
test('should handle para 1', () => {
const code = `SetData("等级", level + 1);`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
// 函数参数需要拆分, 逐个参数捕获
expect(targetSnippet.originalCode).toBe(`"等级"`);
expect(targetSnippet.convertedCode).toBe(`"等级"`);
expect(targetSnippet.literals).toEqual([
"等级"
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 处理C#中函数调用参数中包含普通字符串的情况
test('should handle para 2', () => {
const code = `bbb.Func2("等级", level, level + 1, false, isMax);`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`"等级"`);
expect(targetSnippet.convertedCode).toBe(`"等级"`);
expect(targetSnippet.literals).toEqual([
"等级"
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 处理C#中switch语句中包含普通字符串的情况
test('should handle switch 1', () => {
const code = `
switch(condition){
default:
Log.Warning("实时更新自:" + rankData.Type);
break;
}`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`"实时更新自:" + rankData.Type`);
// 普通函数参数不需要追加 `.TR()`, 除非包含由 `+` 符号连接字符串等的情况
expect(targetSnippet.convertedCode).toBe(`"实时更新自:" + rankData.Type`);
expect(targetSnippet.literals).toEqual([
"实时更新自:"
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 处理C#中switch语句中包含普通字符串的情况
test('should handle switch 2', () => {
const code = `
switch(kwjkwlje){
case (int)RANK_TYPE.天梯段位:
Debug.Log1("jkwhfehwfjkh:" + rank2Data.T4zype);
break;
}`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量, switch 语句中包含普通字符串的情况
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`"jkwhfehwfjkh:" + rank2Data.T4zype`);
expect(targetSnippet.convertedCode).toBe(`"jkwhfehwfjkh:" + rank2Data.T4zype`);
expect(targetSnippet.literals).toEqual([
"jkwhfehwfjkh:"
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 处理C#中switch语句中包含普通字符串的情况
test('should handle switch 3', () => {
const code = `
switch(lkw){
case V434:
{
Action2("wegwfw:" + varnws);
break;
}
}`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`"wegwfw:" + varnws`);
expect(targetSnippet.convertedCode).toBe(`"wegwfw:" + varnws`);
expect(targetSnippet.literals).toEqual([
"wegwfw:"
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 处理C#中switch语句中包含普通字符串的情况
test('should handle switch 4', () => {
const code = `
case V434:
Action2("wegwfw:" + varnws);
break;`
const snippets = extractor.extractStrings(code);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThan(0);
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`"wegwfw:" + varnws`);
expect(targetSnippet.convertedCode).toBe(`"wegwfw:" + varnws`);
expect(targetSnippet.literals).toEqual([
"wegwfw:"
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 处理 C#内容中存在多个相同的包含字符串的语句时, 要每个都提取出来
test('should handle lost 1', () => {
const code = readFileSync('./test/KeeperDialog.cs', 'utf8');
const snippets = extractor.extractStrings(code);
let targetSnippet = snippets[3];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"已购买";
m_btn_buy.enabled = false;
//购买后自动启用`));
expect(targetSnippet.originalCode).toBe(`"已购买"`);
expect(targetSnippet.convertedCode).toBe(`"已购买".TR()`);
expect(targetSnippet.literals).toEqual([
"已购买"
]);
expect(targetSnippet.isChanged).toBe(true);
// 验证提取的片段数量
expect(snippets.length).toBeGreaterThanOrEqual(5)
});
// 字符串中包含 `)` 符号时, 也需要捕获该字符串
test('should handle lost 2', () => {
const code = `m_text_runeSlot.text = Tr.Format(")", _sutraCardData.Card.RuneOpenCount);`;
const snippets = extractor.extractStrings(code);
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`));
expect(targetSnippet.originalCode).toBe(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format(")", _sutraCardData.Card.RuneOpenCount)`);
// 字符串中包含 `)` 符号时, 也需要捕获该字符串
expect(targetSnippet.literals).toEqual([
')'
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 处理 C#内容时, 需要匹配出包含 `(` 符号的字符串表达式
test('should handle lost 3', () => {
const code = `m_text_runeSlot.text = ")";`;
const snippets = extractor.extractStrings(code);
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`")"`));
expect(targetSnippet.originalCode).toBe(`")"`);
expect(targetSnippet.convertedCode).toBe(`")".TR()`);
expect(targetSnippet.literals).toEqual([
')'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
test('should handle return statement 1', () => {
const code = `return "参数错误";`;
const snippets = extractor.extractStrings(code);
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"参数错误"`));
expect(targetSnippet.originalCode).toBe(`"参数错误"`);
expect(targetSnippet.convertedCode).toBe(`"参数错误"`);
expect(targetSnippet.literals).toEqual([
'参数错误'
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
test('should handle return statement 2', () => {
const code = `return "";`;
const snippets = extractor.extractStrings(code);
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`""`));
expect(targetSnippet.originalCode).toBe(`""`);
expect(targetSnippet.convertedCode).toBe(`""`);
expect(targetSnippet.literals).toEqual([
''
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
test('should handle return statement 3', () => {
const code = `return $"";`;
const snippets = extractor.extractStrings(code);
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$""`));
expect(targetSnippet.originalCode).toBe(`$""`);
expect(targetSnippet.convertedCode).toBe(`$""`);
expect(targetSnippet.literals).toEqual([
''
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
test('should handle return statement 4', () => {
const code = `return $"fwefwe";`;
const snippets = extractor.extractStrings(code);
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"fwefwe"`));
expect(targetSnippet.originalCode).toBe(`$"fwefwe"`);
expect(targetSnippet.convertedCode).toBe(`$"fwefwe"`);
expect(targetSnippet.literals).toEqual([
'fwefwe'
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 测试处理C# `return` 语句中的字符串表达式, 提取的字符串表达式中不需要包含 `return` 关键字, 只需要包含字符串表达式
test('should handle return statement 5', () => {
const code = `return $"wefwf{fwef}";`;
const snippets = extractor.extractStrings(code);
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"wefwf{fwef}"`));
expect(targetSnippet.originalCode).toBe(`$"wefwf{fwef}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("wefwf{0}", fwef)`);
expect(targetSnippet.literals).toEqual([
'wefwf{0}'
]);
expect(targetSnippet.isChanged).toBe(true);
});
// 测试处理C#字符串表达式中包含各种特殊字符的情况
test('should handle special characters in string expression 1', () => {
const code = `return "abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\";`;
const snippets = extractor.extractStrings(code);
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`"abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.literals).toEqual([
'abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+{}|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(false);
});
// 测试处理在C#类成员初始赋值语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
test('should handle member initial assignment', () => {
const code = readFileSync("test/TestSpecialString.cs", "utf8");
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"\\\\"`));
expect(targetSnippet.originalCode).toBe(`"\\\\"`);
expect(targetSnippet.convertedCode).toBe(`"\\\\"`);
expect(targetSnippet.literals).toEqual([
'\\\\'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[1];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"\\n"`));
expect(targetSnippet.originalCode).toBe(`"\\n"`);
expect(targetSnippet.convertedCode).toBe(`"\\n"`);
expect(targetSnippet.literals).toEqual([
'\\n'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[2];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"\\t"`));
expect(targetSnippet.originalCode).toBe(`$"\\t"`);
expect(targetSnippet.convertedCode).toBe(`$"\\t"`);
expect(targetSnippet.literals).toEqual([
'\\t'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
test('should handle special characters in string expression 3', () => {
const code = readFileSync("test/TestSpecialString.cs", "utf8");
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[3];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.literals).toEqual([
'1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[4];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`"2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.literals).toEqual([
'2. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[6];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`"4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.literals).toEqual([
'4. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
test('should handle special characters in string expression 3.1', () => {
const code = readFileSync("test/TestSpecialString.cs", "utf8");
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[3];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`"1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.literals).toEqual([
'1. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
test('should handle special characters in string expression of return sentence', () => {
const code = readFileSync("test/TestSpecialString.cs", "utf8");
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[7];
// 需要统一匹配 `return` 语句中的字符串表达式
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`"5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.literals).toEqual([
'5. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
test('should handle special characters in string expression 3.2', () => {
const code = readFileSync("test/TestSpecialString.cs", "utf8");
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[8];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`"6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.literals).toEqual([
'6. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[9];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`"7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.literals).toEqual([
'7. {}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 测试处理在C#各种语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#字符串边界识别和捕获
test('should handle special characters in string expression 4', () => {
const code = readFileSync("test/TestSpecialString.cs", "utf8");
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[5];
// 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"3. {wef}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`$"3. {wef}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("3. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wef)`);
expect(targetSnippet.literals).toEqual([
'3. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 测试处理在C#各种形式语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#内插字符串边界识别和捕获
test('should handle special characters in string expression 5', () => {
const code = readFileSync("test/TestSpecialString.cs", "utf8");
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[10];
// 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"8. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`$"8. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("8. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
expect(targetSnippet.literals).toEqual([
'8. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 测试处理在C#各种形式语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#内插字符串边界识别和捕获
test('should handle special characters in string expression 6', () => {
const code = readFileSync("test/TestSpecialString.cs", "utf8");
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[11];
// 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"9. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`$"9. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("9. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
expect(targetSnippet.literals).toEqual([
'9. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 测试处理在C#各种形式语句中, 字符串表达式中包含各种特殊字符的情况, 工具中需要统一处理C#内插字符串边界识别和捕获
test('should handle special characters in string expression 7', () => {
const code = readFileSync("test/TestSpecialString.cs", "utf8");
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[12];
// 需要支持内插字符串中的特殊转义字符组合: `{{` 表示 `{`, `}}` 表示 `}`, 但是 `{{`和`}}` 从内插字符串转 `Tr.Format(...)` 形式时, 不需要转为 `{`和`}`, `{xxx}` 表示内插表达式
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"10. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`$"10. {wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
// 需要统一匹配 `return` 语句中的字符串表达式
expect(targetSnippet.convertedCode).toBe(`Tr.Format("10. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
expect(targetSnippet.literals).toEqual([
'10. {0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
test('should handle optional value assignment', () => {
const code = `m_text_otherName.text = battleModel.TeammatePlayerData?.Name ?? "";`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`battleModel.TeammatePlayerData?.Name ?? ""`));
expect(targetSnippet.originalCode).toBe(`battleModel.TeammatePlayerData?.Name ?? ""`);
expect(targetSnippet.convertedCode).toBe(`battleModel.TeammatePlayerData?.Name ?? "".TR()`);
expect(targetSnippet.literals).toEqual([
''
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
test('should handle string in comment 1', () => {
const code = `
protected override void OnShow(object param)
{
// Log.Info("AAA:ChooseSutraLayer显示1");
base.OnShow(param);
}`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"AAA:ChooseSutraLayer显示1"`));
expect(targetSnippet.originalCode).toBe(`"AAA:ChooseSutraLayer显示1"`);
expect(targetSnippet.convertedCode).toBe(`"AAA:ChooseSutraLayer显示1"`);
expect(targetSnippet.literals).toEqual([
'AAA:ChooseSutraLayer显示1'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
test('should handle string in comment 2', () => {
const code = `
protected override void OnShow(object param)
{
// Log.Info($"BBB:Jowikwf");
base.OnShow(param);
}`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"BBB:Jowikwf"`));
expect(targetSnippet.originalCode).toBe(`$"BBB:Jowikwf"`);
expect(targetSnippet.convertedCode).toBe(`$"BBB:Jowikwf"`);
expect(targetSnippet.literals).toEqual([
'BBB:Jowikwf'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
test('should handle string in comment 3', () => {
const code = `
protected override void OnShow(object param)
{
// Log.Info($"BBB:{wlek}Jowikwf");
base.OnShow(param);
}`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"BBB:{wlek}Jowikwf"`));
expect(targetSnippet.originalCode).toBe(`$"BBB:{wlek}Jowikwf"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("BBB:{0}Jowikwf", wlek)`);
expect(targetSnippet.literals).toEqual([
'BBB:{0}Jowikwf'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
test('should handle string in comment 4', () => {
const code = `
protected override void OnShow(object param)
{
// Log.Info("{}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\");
base.OnShow(param);
}`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"{}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`"{}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`"{}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.literals).toEqual([
'{}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
test('should handle string in comment 5', () => {
const code = `
protected override void OnShow(object param)
{
// Log.Info($"{wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\");
base.OnShow(param);
}`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"{wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`));
expect(targetSnippet.originalCode).toBe(`$"{wefff}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\", wefff)`);
expect(targetSnippet.literals).toEqual([
'{0}{{}}abcdefghijklmnopqrstuvwxyz\`-=[];\',./~!@#$%^&*()_+|:\\"<>?1234567890\\\\'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
test('should handle complex string 1', () => {
const code = `
infoStr += $"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n";
infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`));
expect(targetSnippet.originalCode).toBe(`$"伤害类型: [color={colorCode}][b]{_damageInfo.DamageType}[/b][/color]\n"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
expect(targetSnippet.literals).toEqual([
'伤害类型: [color={0}][b]{1}[/b][/color]\n'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
test('should handle complex string 2', () => {
const code = `
infoStr += Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType);
infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`));
expect(targetSnippet.originalCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("伤害类型: [color={0}][b]{1}[/b][/color]\n", colorCode, _damageInfo.DamageType)`);
expect(targetSnippet.literals).toEqual([
'伤害类型: [color={0}][b]{1}[/b][/color]\n'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 正确处理C# 字符串表达式中递归内嵌字符串表达式的情况: 需要正确转换内插字符串格式
test('should handle fix interpolated string nested literals', () => {
const code = `infoStr += $"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n";`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n"`));
expect(targetSnippet.originalCode).toBe(`$"是否暴击: [b]{(_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否")}[/b]\n"`);
// 需要正确转换内插字符串格式
expect(targetSnippet.convertedCode).toBe(`Tr.Format("是否暴击: [b]{0}[/b]\n", (_damageInfo.IsCritical ? "[color=#00B000]是[/color]" : "否"))`);
expect(targetSnippet.literals).toEqual([
'是否暴击: [b]{0}[/b]\n'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
test('should handle complex string with optional value 3', () => {
const code = `
else if (battleModel.CurGameType == BattleModel.GameType.Teamwork)
{
//合作模式显示队友名称
m_text_otherName.text = battleModel.TeammatePlayerData?.Name ?? "";
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`battleModel.TeammatePlayerData?.Name ?? ""`));
expect(targetSnippet.originalCode).toBe(`battleModel.TeammatePlayerData?.Name ?? ""`);
expect(targetSnippet.convertedCode).toBe(`battleModel.TeammatePlayerData?.Name ?? "".TR()`);
expect(targetSnippet.literals).toEqual([
''
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
test('should handle complex string 4', () => {
const code = `
private void OnBtnIaa(EventContext context)
{
var iaaConfig = IAAConfigTable.GetConfigById((int)IAAId.境界失败_加速冷却);
if (iaaConfig == null)
{
return;
}
string des = $"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟";
this.GetModel().ShowAdWithDialog(IAAId.境界失败_加速冷却, "加速时间", des, null);
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟"`));
expect(targetSnippet.originalCode).toBe(`$"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("观看视频后\n等待时间减少{0}分钟", iaaConfig.EffectValue / 60)`);
expect(targetSnippet.literals).toEqual([
'观看视频后\n等待时间减少{0}分钟'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 需要正确识别函数调用参数中每个参数的边界, 并识别每个参数中的字符串表达式
test('should handle strings in function call arguments', () => {
const code = `
private void OnBtnIaa(EventContext context)
{
var iaaConfig = IAAConfigTable.GetConfigById((int)IAAId.境界失败_加速冷却);
if (iaaConfig == null)
{
return;
}
string des = $"观看视频后\n等待时间减少{iaaConfig.EffectValue / 60}分钟";
this.GetModel().ShowAdWithDialog(IAAId.境界失败_加速冷却, "加速时间", des, null);
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[1];
// 需要正确识别函数调用参数中每个参数的边界, 并识别每个参数中的字符串表达式
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"加速时间"`));
expect(targetSnippet.originalCode).toBe(`"加速时间"`);
expect(targetSnippet.convertedCode).toBe(`"加速时间"`);
expect(targetSnippet.literals).toEqual([
'加速时间'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 正确处理C#注释中的字符串表达式 - 同样需要从字符串中提取字符串表达式信息
test('should handle string in comment 5', () => {
const code = `
m_text_percent.text = $"{curGoodsConfig.RebateRate}%";
m_name_holder.url = FUISys.Instance.GetIconUrl(_curShopDetail.ShopConfig.Name);
m_thumbnail_holder.url = FUISys.Instance.GetIconUrl(_curShopDetail.ShopConfig.Thumbnail);
m_btn_buy.text = $"¥ {curGoodsConfig.Price}";
m_text_timeleft.text = curShop.TimeLimitData.RemainTime().TR() + "后消失".TR();
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[2];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`));
expect(targetSnippet.originalCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
expect(targetSnippet.convertedCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
expect(targetSnippet.literals).toEqual([
'后消失'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 正确处理C# `switch` 表达式形式中的字符串表达式
test('should handle switch expression string', () => {
const code = `
case (int)State.Lock:
Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
break;
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
expect(targetSnippet.literals).toEqual([
'灵田{0}级解锁'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 正确识别C# 行注释语句边界, 行注释不能影响上下行的字符串提取
test('should handle comment boundary 1', () => {
const code = `
//上锁
case (int)State.Lock:
Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
break;
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
expect(targetSnippet.literals).toEqual([
'灵田{0}级解锁'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 正确识别C# 行注释语句边界, 行注释不能影响上下行的字符串提取
test('should handle comment boundary 2', () => {
const code = `
// klwjfe
namespace TestNamespace{
// lkwjfe
public class FK{
// klwf
public void TestM(){
// 模拟状态变量
var _state = (int)State.Lock;
// 模拟土地ID变量
switch (_state){
//上锁
case (int)State.Lock:
// lkwjefl
Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
// klwejf
break;
// lkwjf
}
// klwjfel
}
}
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
expect(targetSnippet.literals).toEqual([
'灵田{0}级解锁'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 正确识别C# 行注释语句边界, 行注释不能影响上下行的字符串提取
test('should handle comment boundary 3', () => {
const code = `
// klwjfe
namespace TestNamespace{
// lkwjfe
public class FK{
// wlkjelj
private int Llkwe = 23;
// lkjlkj
private int Dwekl { get; set; } = 23;
// jklwef
private static int Dwekl { get; set; } = 23;
// lkwjeflk
public static void TestM(){}
// wesd
public static void TestM()
{
}
// wesd
public static void TestM(){
}
// klwf
public void TestM(){
// 模拟状态变量
var _state = (int)State.Lock;
// 模拟土地ID变量
switch (_state){
//上锁
case (int)State.Lock:
// lkwjefl
Toast.Info($"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁");
// klwejf
break;
// lkwjf
}
// klwjfel
}
}
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`));
expect(targetSnippet.originalCode).toBe(`$"灵田{_model.GetFarmLandUnlockLevel(_landId)}级解锁"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("灵田{0}级解锁", _model.GetFarmLandUnlockLevel(_landId))`);
expect(targetSnippet.literals).toEqual([
'灵田{0}级解锁'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 正确识别C#文档注释边界, 类、方法、类成员的文档注释需要直接忽略捕获, 不参与字符串表达式提取; 文档注释通常位于类、方法、类成员的定义上方, 文档注释的格式形如: ` /// yyy\n /// ` 或 ` /// `, 其中xxx为标签, yyy为注释内容, ``和`` 不一定在同一行或不同行, ` /// yyy\n` 不一定只有一行; 文档注释通常以``标签开始, 以``标签结束, 或者以 `` 结束。
test('should handle doc comment boundary for class/method/members', () => {
const code = `
///
/// "fwef 类注释1"
/// $"fwef 类注释2"
/// $"fwef 类注释2 {wef}"
/// wef $"fwef 类注释2 {wef}" wef
///
///
public partial class TimeLimitedChargeDialog
{
///
/// $" Property成员注释 dsvdf {wlpoe}"
///
private int Ljfw { get; set; } = 342;
///
/// $" Field成员注释 wfewe {vvwe}"
/// $" Field成员注释 wfewe {vvwe}" wfe
///
private int Ljfw = 342;
///
///
private int Ljfw = 342;
///
/// " wef方法注释 wef"
/// $" wef方法注释 wef"
/// $" wef方法注释 wef{wef}"
/// wfe $" wef方法注释 wef{wef}"
/// wfe $" wef方法注释 wef{wef}" wef
///wfe $" wef方法注释 wef{wef}" wef
/// wefw $" wef方法注释 wef{wef}" we
///
///
/// 是否重新创建页签列表
private void RefreshGoodsInfo()
{
m_text_timeleft.text = curShop.TimeLimitData.RemainTime() + "后消失";
}
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`curShop.TimeLimitData.RemainTime() + "后消失"`));
expect(targetSnippet.originalCode).toBe(`curShop.TimeLimitData.RemainTime() + "后消失"`);
expect(targetSnippet.convertedCode).toBe(`curShop.TimeLimitData.RemainTime().TR() + "后消失".TR()`);
expect(targetSnippet.literals).toEqual([
'后消失'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 正确识别C#方法参数中的字符串表达式, 支持1到多个形参有默认值的情形
test('should handle string expression in method call parameter 1', () => {
const code = `
namespace FaBao.UI.Comp
{
public partial class ItemCommonComp
{
public void BindData(bool needCompare = false)
{
m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
}
}
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
expect(targetSnippet.literals).toEqual([
'{0}阶'
]);
expect(targetSnippet.isChanged).toBe(false);
}
expect(snippets.length).toBe(1);
});
// 正确识别C#方法参数中的字符串表达式, 支持1到多个形参有默认值的情形
test('should handle string expression in method call parameter 2', () => {
const code = `
namespace FaBao.UI.Comp
{
public partial class ItemCommonComp
{
public void BindData(bool needCompare = false, long needCompare2 = 23, float needCompare2 = 324.0)
{
m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
}
}
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
expect(targetSnippet.literals).toEqual([
'{0}阶'
]);
expect(targetSnippet.isChanged).toBe(false);
}
expect(snippets.length).toBe(1);
});
// 正确识别C#方法参数中的字符串表达式, 支持1到多个形参有默认值的情形
test('should handle string expression in method call parameter 3', () => {
const code = `
namespace FaBao.UI.Comp
{
public partial class ItemCommonComp
{
public void BindData(string wkle, bool needCompare = false, int needCompare2 = false)
{
m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
}
}
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
expect(targetSnippet.literals).toEqual([
'{0}阶'
]);
expect(targetSnippet.isChanged).toBe(false);
}
expect(snippets.length).toBe(1);
});
// 正确提取C#方法参数中的字符串表达式信息, 支持1到多个形参有默认值的情形
test('should handle string expression in method call parameter 4', () => {
const code = `
namespace FaBao.UI.Comp
{
public partial class ItemCommonComp
{
public void BindData(string wkle = "11111", bool needCompare = false, int needCompare2 = false)
{
}
public void BindData(string wkle = "22", string wkle = "werwer", bool needCompare = false, string wkle = "wefwewef31f", int needCompare2 = false, string wkle = "df223")
{
}
public void BindData(string wkle = "(*&%\\\\*(@Fwfe))&(")
{
}
public void BindData(string we, string wkle = "aaaaa", bool needCompare = false, int needCompare2 = false)
{
m_text_level.text = Tr.Format("{0}阶", _itemConfig.PillLevel);
}
}
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"11111"`));
expect(targetSnippet.originalCode).toBe(`"11111"`);
expect(targetSnippet.convertedCode).toBe(`"11111"`);
expect(targetSnippet.literals).toEqual([
'11111'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[1];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"22"`));
expect(targetSnippet.originalCode).toBe(`"22"`);
expect(targetSnippet.convertedCode).toBe(`"22"`);
expect(targetSnippet.literals).toEqual([
'22'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[2];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"werwer"`));
expect(targetSnippet.originalCode).toBe(`"werwer"`);
expect(targetSnippet.convertedCode).toBe(`"werwer"`);
expect(targetSnippet.literals).toEqual([
'werwer'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[3];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"wefwewef31f"`));
expect(targetSnippet.originalCode).toBe(`"wefwewef31f"`);
expect(targetSnippet.convertedCode).toBe(`"wefwewef31f"`);
expect(targetSnippet.literals).toEqual([
'wefwewef31f'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[4];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"df223"`));
expect(targetSnippet.originalCode).toBe(`"df223"`);
expect(targetSnippet.convertedCode).toBe(`"df223"`);
expect(targetSnippet.literals).toEqual([
'df223'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[5];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"(*&%\\\\*(@Fwfe))&("`));
expect(targetSnippet.originalCode).toBe(`"(*&%\\\\*(@Fwfe))&("`);
expect(targetSnippet.convertedCode).toBe(`"(*&%\\\\*(@Fwfe))&("`);
expect(targetSnippet.literals).toEqual([
'(*&%\\\\*(@Fwfe))&('
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[6];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"aaaaa"`));
expect(targetSnippet.originalCode).toBe(`"aaaaa"`);
expect(targetSnippet.convertedCode).toBe(`"aaaaa"`);
expect(targetSnippet.literals).toEqual([
'aaaaa'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[7];
// 类注释和方法注释不参与字符串表达式提取, 不影响字符串提取结果
expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}阶", _itemConfig.PillLevel)`));
expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}阶", _itemConfig.PillLevel)`);
expect(targetSnippet.literals).toEqual([
'{0}阶'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 需要正确识别C#代码中 `if` 语句边界 和 创建对象表达式的边界, 识别 `if` 语句前、中、后部分中的字符串表达式
test('should handle if border and object creation expression border', () => {
const code = `
namespace FaBao.UI.MainUI
{
public partial class SpellUpgradeDialog
{
private async void OnBtnUpgrade()
{
// new SpellCard_Action_UpgradeSpellCard(_symbolCard.Id) 被方法调用传参的 () 包裹, 因此已经构成了完整的对象创建表达式
if (await this.GetModel().DispatchAction(new SpellCard_Action_UpgradeSpellCard(_symbolCard.Id)))
{
//升级成功后刷新界面
m_text_level.text = Tr.Format("{0}级", _symbolCard.Level);
}
}
}
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
// 需要正确识别 `if` 语句边界, 识别 `if` 语句前、中、后部分中的字符串表达式
expect(targetSnippet.originalIndex).toBe(code.indexOf(`Tr.Format("{0}级", _symbolCard.Level)`));
expect(targetSnippet.originalCode).toBe(`Tr.Format("{0}级", _symbolCard.Level)`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("{0}级", _symbolCard.Level)`);
expect(targetSnippet.literals).toEqual([
'{0}级'
]);
expect(targetSnippet.isChanged).toBe(false);
}
expect(snippets.length).toBe(1);
});
// 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
test('should handle class member initialization assignment with anonymous function 1', () => {
const code = `
FUISys.Instance.ShowLayer(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
{
Title = "提示",
ConfirmCallback = async () =>
{
//灵纹一键下阵
if (await this.GetModel().DispatchAction(new Rune_Action_CleanRune(_sutraCardData.Card.Id)))
{
Toast.Info("卸载成功");
}
},
CancelText = "取消",
}).Forget();
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"提示"`));
expect(targetSnippet.originalCode).toBe(`"提示"`);
expect(targetSnippet.convertedCode).toBe(`"提示"`);
expect(targetSnippet.literals).toEqual([
'提示'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[1];
// 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
expect(targetSnippet.literals).toEqual([
'卸载成功'
]);
expect(targetSnippet.isChanged).toBe(false);
}
{
let targetSnippet = snippets[2];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"取消"`));
expect(targetSnippet.originalCode).toBe(`"取消"`);
expect(targetSnippet.convertedCode).toBe(`"取消"`);
expect(targetSnippet.literals).toEqual([
'取消'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
test('should handle class member initialization assignment with anonymous function 2', () => {
const code = `
FUISys.Instance.ShowLayer(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
{
ConfirmCallback = async () =>
{
Toast.Info("卸载成功");
},
}).Forget();
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
// 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
expect(targetSnippet.literals).toEqual([
'卸载成功'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
test('should handle class member initialization assignment with anonymous function 3', () => {
const code = `
FUISys.Instance.ShowLayer(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
{
ConfirmCallback = (int qwfewe) =>
{
Toast.Info($"卸载成功: {qwfewe}");
},
}).Forget();
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
// 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"卸载成功: {qwfewe}"`));
expect(targetSnippet.originalCode).toBe(`$"卸载成功: {qwfewe}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("卸载成功: {0}", qwfewe)`);
expect(targetSnippet.literals).toEqual([
'卸载成功: {0}'
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
test('should handle class member initialization assignment with anonymous function 4', () => {
const code = `
FUISys.Instance.ShowLayer(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
{
ConfirmCallback = async () => Toast.Info("卸载成功"),
}).Forget();
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
// 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
expect(targetSnippet.literals).toEqual([
'卸载成功'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 需要正确识别C#代码中的匿名函数, 匿名函数中的字符串表达式也同样需要提取; 匿名函数通常以 `0到多个修饰词 (可选参数列表) => { ... }` 或者 `0到多个修饰词 (可选参数列表) => 一句表达式` 形式定义实现.
test('should handle class member initialization assignment with anonymous function 5', () => {
const code = `
FUISys.Instance.ShowLayer(UISceneType.Main, new MainConfirmDialog.MainConfirmDialogParam()
{
ConfirmCallback = () => Toast.Info("卸载成功"),
}).Forget();
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
// 需要正确识别C#代码中类成员初始化赋值时, 给成员赋值匿名函数时, 匿名函数中的字符串表达式也需要提取
expect(targetSnippet.originalIndex).toBe(code.indexOf(`"卸载成功"`));
expect(targetSnippet.originalCode).toBe(`"卸载成功"`);
expect(targetSnippet.convertedCode).toBe(`"卸载成功"`);
expect(targetSnippet.literals).toEqual([
'卸载成功'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 测试处理 `xxx.text = yyy;` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式; 如果`yyy`中不存在字符串, 则`yyy` 中以 `+` 连接的表达式需要加上 `.TR()`; 如果 `yyy` 是作为值表达式整体(必定返回字符串类型值), 也需要追加 `.TR()`。
test('should handle .text = format 1', () => {
const code = `m_text_iwff.text = info ;`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`info`));
expect(targetSnippet.originalCode).toBe(`info`);
expect(targetSnippet.convertedCode).toBe(`info.TR()`);
expect(targetSnippet.literals).toEqual([
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 测试处理 `xxx.text = yyy;` 形式赋值语句, 以 `.text =` 给 `text` 成员赋值的表达式中, `yyy` 部分必定是字符串表达式; 如果`yyy`中不存在字符串, 则`yyy` 中以 `+` 连接的表达式需要加上 `.TR()`; 如果 `yyy` 是作为值表达式整体(必定返回字符串类型值), 也需要追加 `.TR()`。
test('should handle .text = format 2', () => {
const code = `m_text_info.text = info1.member1 + info2.member2;`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`info1.member1 + info2.member2`));
expect(targetSnippet.originalCode).toBe(`info1.member1 + info2.member2`);
expect(targetSnippet.convertedCode).toBe(`info1.member1.TR() + info2.member2.TR()`);
expect(targetSnippet.literals).toEqual([
]);
expect(targetSnippet.isChanged).toBe(true);
}
});
// 处理字符串出现在赋值表达式的 `=` 左边(左值表达式)的情况
test('should handle right value expression', () => {
const code = `
((GLoader)GetChild($"m_loader_dailyItem{i + 1}")).url = FUISys.Instance.GetIconUrl(ItemTable.GetConfigById(_cardConfig.EveryDayAwards[i].ResId).Icon);
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_loader_dailyItem{i + 1}"`));
expect(targetSnippet.originalCode).toBe(`$"m_loader_dailyItem{i + 1}"`); // 左边表达式函数调用参数中的字符串表达式
expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_loader_dailyItem{0}", i + 1)`);
expect(targetSnippet.literals).toEqual([
`m_loader_dailyItem{0}`
]);
expect(targetSnippet.isChanged).toBe(true);
}
expect(snippets.length).toBe(1);
});
// 支持处理字符串里出现各种特殊符号的情况
test('should handle special characters in string 8', () => {
const code = `
m_btn_paid.text = $"
{paidConfig.Quantity}";
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"
{paidConfig.Quantity}"`));
expect(targetSnippet.originalCode).toBe(`$"
{paidConfig.Quantity}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("
{0}", paidConfig.Quantity)`);
expect(targetSnippet.literals).toEqual([
`
{0}`
]);
expect(targetSnippet.isChanged).toBe(true);
}
expect(snippets.length).toBe(1);
});
// 处理赋值表达式 `=` 左右两边(左值表达式和右值表达式)都存在字符串的情况
test('should handle assignment expression with both sides strings', () => {
const code = `
((GTextField)GetChild($"m_text_dailyCount{i + 1}")).text = $"x{_cardConfig.EveryDayAwards[i].Count}";
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_text_dailyCount{i + 1}"`));
expect(targetSnippet.originalCode).toBe(`$"m_text_dailyCount{i + 1}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_text_dailyCount{0}", i + 1)`);
expect(targetSnippet.literals).toEqual([
`m_text_dailyCount{0}`
]);
expect(targetSnippet.isChanged).toBe(true);
}
{
let targetSnippet = snippets[1];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"x{_cardConfig.EveryDayAwards[i].Count}"`));
expect(targetSnippet.originalCode).toBe(`$"x{_cardConfig.EveryDayAwards[i].Count}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("x{0}", _cardConfig.EveryDayAwards[i].Count)`);
expect(targetSnippet.literals).toEqual([
`x{0}`
]);
expect(targetSnippet.isChanged).toBe(true);
}
expect(snippets.length).toBe(2);
});
// 支持处理字符串里出现各种特殊符号的情况
test('should handle special characters in string 9', () => {
const code = `
(GetChild($"m_textAnime{GetNextNumber()}") as TurtleTextAnimeComp)?.SetText(word);
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_textAnime{GetNextNumber()}"`));
expect(targetSnippet.originalCode).toBe(`$"m_textAnime{GetNextNumber()}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_textAnime{0}", GetNextNumber())`);
expect(targetSnippet.literals).toEqual([
`m_textAnime{0}`
]);
expect(targetSnippet.isChanged).toBe(true);
}
expect(snippets.length).toBe(1);
});
// 支持处理字符串里出现各种特殊符号的情况
test('should handle special characters in string 10', () => {
const code = `
(GetChild($"m_textAnime{GetNextNumber()}") as TurtleTextAnimeComp)?.SetText(word);
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_textAnime{GetNextNumber()}"`));
expect(targetSnippet.originalCode).toBe(`$"m_textAnime{GetNextNumber()}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_textAnime{0}", GetNextNumber())`);
expect(targetSnippet.literals).toEqual([
`m_textAnime{0}`
]);
expect(targetSnippet.isChanged).toBe(true);
}
expect(snippets.length).toBe(1);
});
// 修正字符串表达式捕获范围
test('should handle string expression capture range 1', () => {
const code = `
(GetChild($"vwvwe{index + 1}") as LargeLotterySpellCardComp)?.InitData(spellCardId, true);
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"vwvwe{index + 1}"`));
expect(targetSnippet.originalCode).toBe(`$"vwvwe{index + 1}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("vwvwe{0}", index + 1)`);
expect(targetSnippet.literals).toEqual([
`vwvwe{0}`
]);
expect(targetSnippet.isChanged).toBe(true);
}
expect(snippets.length).toBe(1);
});
// 修正字符串表达式捕获范围
test('should handle string expression capture range 2', () => {
const code = `
(GetChild($"brrr_{index + 1}") as LargeLotterySpellCardComp)?.InitData(spellCardId, true);
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"brrr_{index + 1}"`));
expect(targetSnippet.originalCode).toBe(`$"brrr_{index + 1}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("brrr_{0}", index + 1)`);
expect(targetSnippet.literals).toEqual([
`brrr_{0}`
]);
expect(targetSnippet.isChanged).toBe(true);
}
expect(snippets.length).toBe(1);
});
// 修正字符串表达式捕获范围
test('should handle string expression capture range 3', () => {
const code = `
(GetChild($"m_card_{index}") as LargeLotterySpellCardComp)?.PlayEff();
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`$"m_card_{index}"`));
expect(targetSnippet.originalCode).toBe(`$"m_card_{index}"`);
expect(targetSnippet.convertedCode).toBe(`Tr.Format("m_card_{0}", index)`);
expect(targetSnippet.literals).toEqual([
`m_card_{0}`
]);
expect(targetSnippet.isChanged).toBe(true);
}
expect(snippets.length).toBe(1);
});
// 字符串表达式字面量直接为null时, 无需转换null
test('should handle null 1', () => {
const code = `m_text_notice.text = null;`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`null`));
expect(targetSnippet.originalCode).toBe(`null`);
expect(targetSnippet.convertedCode).toBe(`null`);
expect(targetSnippet.literals).toEqual([
]);
expect(targetSnippet.isChanged).toBe(false);
}
expect(snippets.length).toBe(1);
});
// 字符串表达式字面量直接为null时, 无需转换null
test('should handle null 2', () => {
const code = `m_text_notice.wfefe = null;`;
const snippets = extractor.extractStrings(code);
expect(snippets.length).toBe(0);
});
// 需要正确获取 `originalIndex`, `originalIndex` 的定义为 `originalIndex=code.indexOf(originalCode)`, 其中 `code` 表示原始代码, `originalCode` 表示匹配出的原始代码中的字符串表达式
test('should handle calculate originalIndex', () => {
const code = `
namespace FaBao
{
public partial class UserInfoModel
{
public bool HasJoinedGuild()
{
return !string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0";
}
public void UpdateBargain(BargainShopData bargainShopData)
{
MyGuildInfo.IsBargainShopBargain = true;
}
}
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalCode).toBe(`!string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0"`);
expect(targetSnippet.originalIndex).toBe(code.indexOf(`!string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0"`));
expect(targetSnippet.convertedCode).toBe(`!string.IsNullOrEmpty(MyGuildInfo.GuildId) && MyGuildInfo.GuildId != "0"`);
expect(targetSnippet.literals).toEqual([
'0'
]);
expect(targetSnippet.isChanged).toBe(false);
}
});
// 测试 `xxx.text = yyy;` 和 `m_btn_xxx.title = yyy;` 形式的赋值语句都需要支持中文, 包括字符串表达式中也要支持包含中文的情况
test('should handle .text = assignments contains chinese', () => {
const code = `
为栓饭cxs12.text = 南方eflkj.d文件 + "完善ew";
m_btn_为栓饭cxs12.title = bw尅kljekl.完善 + "栏栏nfc";
`;
const snippets = extractor.extractStrings(code);
{
let snippet = snippets[0];
expect(snippet.originalCode).toBe('南方eflkj.d文件 + "完善ew"');
expect(snippet.convertedCode).toBe('南方eflkj.d文件.TR() + "完善ew".TR()');
expect(snippet.literals).toEqual([
'完善ew'
]);
}
{
let snippet = snippets[1];
expect(snippet.originalCode).toBe('bw尅kljekl.完善 + "栏栏nfc"');
expect(snippet.convertedCode).toBe('bw尅kljekl.完善.TR() + "栏栏nfc".TR()');
expect(snippet.literals).toEqual([
'栏栏nfc'
]);
}
});
// 测试`m_btn_xxx.title = yyy;` 形式的赋值语句要和`xxx.text = yyy;`形式的赋值语句完全等同处理
test('should handle m_btn_xxx.title = yyy assignments contains chinese', () => {
const code = `
switch (_roomData.RoomState)
{
case Data.Match.RoomState.IsDismiss:
m_btn_join.title = "已解散";
m_btn_join.enabled = false;
grayed = true;
break;
case Data.Match.RoomState.Rejected:
{
m_btn_join.title = $"被拒绝{Kff}";
m_btn_join.enabled = false;
break;
}
case Data.Match.RoomState.OutOfDate:
m_dd_join.text = "已到期";
break;
default:
m_btn_join.title = "申请";
m_btn_joi23n.title = $"申请{Kjf}";
break;
}
`;
const snippets = extractor.extractStrings(code);
{
let snippet = snippets[0];
expect(snippet.originalCode).toBe('"已解散"');
expect(snippet.convertedCode).toBe('"已解散".TR()');
expect(snippet.literals).toEqual([
'已解散'
]);
}
{
let snippet = snippets[1];
expect(snippet.originalCode).toBe('$"被拒绝{Kff}"');
expect(snippet.convertedCode).toBe('Tr.Format("被拒绝{0}", Kff)');
expect(snippet.literals).toEqual([
'被拒绝{0}'
]);
}
{
let snippet = snippets[2];
expect(snippet.originalCode).toBe('"已到期"');
expect(snippet.convertedCode).toBe('"已到期".TR()');
expect(snippet.literals).toEqual([
'已到期'
]);
}
{
let snippet = snippets[3];
expect(snippet.originalCode).toBe('"申请"');
expect(snippet.convertedCode).toBe('"申请".TR()');
expect(snippet.literals).toEqual([
'申请'
]);
}
{
let snippet = snippets[4];
expect(snippet.originalCode).toBe('$"申请{Kjf}"');
expect(snippet.convertedCode).toBe('Tr.Format("申请{0}", Kjf)');
expect(snippet.literals).toEqual([
'申请{0}'
]);
}
});
// 测试三元表达式中的字符串表达式处理
test('should handle thriple expression with string expression 1', () => {
const code = `m_btn_confirm.title = isUsed ? "使用中" : "使用";`;
const snippets = extractor.extractStrings(code);
{
let snippet = snippets[0];
expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
expect(snippet.convertedCode).toBe('isUsed ? "使用中".TR() : "使用".TR()');
expect(snippet.literals).toEqual([
'使用中',
'使用'
]);
}
});
// 测试三元表达式中的字符串表达式处理
test('should handle thriple expression with string expression 2', () => {
const code = `wefe.text = isUsed ? "使用中" : "使用";`;
const snippets = extractor.extractStrings(code);
{
let snippet = snippets[0];
expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
expect(snippet.convertedCode).toBe('isUsed ? "使用中".TR() : "使用".TR()');
expect(snippet.literals).toEqual([
'使用中',
'使用'
]);
}
});
// 测试三元表达式中的字符串表达式处理
test('should handle thriple expression with string expression 3', () => {
const code = `wefetext = isUsed ? "使用中" : "使用";`;
const snippets = extractor.extractStrings(code);
{
let snippet = snippets[0];
expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
expect(snippet.convertedCode).toBe('isUsed ? "使用中" : "使用"');
expect(snippet.literals).toEqual([
'使用中',
'使用'
]);
}
});
// 测试三元表达式中的字符串表达式处理
test('should handle thriple expression with string expression 4', () => {
const code = `wefetext(isUsed ? "使用中" : "使用");`;
const snippets = extractor.extractStrings(code);
{
let snippet = snippets[0];
expect(snippet.originalCode).toBe('isUsed ? "使用中" : "使用"');
expect(snippet.convertedCode).toBe('isUsed ? "使用中" : "使用"');
expect(snippet.literals).toEqual([
'使用中',
'使用'
]);
}
});
// 测试if语句有花括号包围的单句表达式语句中的字符串表达式处理
test('should handle assignment expression within if{}else{}', () => {
const code = `
if (_eventIdx < _table.MainDescription.Length)
{
m_text_desc.text = _table.MainDescription[_eventIdx];
}
else
{
m_text_desc.text = _table.MainDescription[0];
}
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[_eventIdx]`));
expect(targetSnippet.originalCode).toBe(`_table.MainDescription[_eventIdx]`);
expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[_eventIdx].TR()`);
expect(targetSnippet.literals).toEqual([
]);
expect(targetSnippet.isChanged).toBe(true);
}
{
let targetSnippet = snippets[1];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[0]`));
expect(targetSnippet.originalCode).toBe(`_table.MainDescription[0]`);
expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[0].TR()`);
expect(targetSnippet.literals).toEqual([
]);
expect(targetSnippet.isChanged).toBe(true);
}
expect(snippets.length).toBe(2);
})
// 测试if语句无花括号包围的单句表达式语句中的字符串表达式处理
test('should handle assignment expression within if-else-end', () => {
const code = `
if (_eventIdx < _table.MainDescription.Length)
m_text_desc.text = _table.MainDescription[_eventIdx];
else
m_text_desc.text = _table.MainDescription[0];
`;
const snippets = extractor.extractStrings(code);
{
let targetSnippet = snippets[0];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[_eventIdx]`));
expect(targetSnippet.originalCode).toBe(`_table.MainDescription[_eventIdx]`);
expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[_eventIdx].TR()`);
expect(targetSnippet.literals).toEqual([
]);
expect(targetSnippet.isChanged).toBe(true);
}
{
let targetSnippet = snippets[1];
expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[0]`));
expect(targetSnippet.originalCode).toBe(`_table.MainDescription[0]`);
expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[0].TR()`);
expect(targetSnippet.literals).toEqual([
]);
expect(targetSnippet.isChanged).toBe(true);
}
expect(snippets.length).toBe(2);
})
// // 测试if语句无花括号包围的单句表达式语句中的字符串表达式处理
// test('should handle assignment expression within if-else-end', () => {
// const code = `
// private void RefreshRedDot()
// {
// m_redDot.visible = _data.NeedShowRedDot();
// }
// private void OnSelfClick()
// {
// this.GetSystem().PlayAudioByName(AudioName.SoundUI_Button);
// if (!_data.IsOpen)
// {
// string tip = _data.Config.LockTip;
// if (_data.Config.TaskIds != null && _data.Config.TaskIds.Count > 0)
// {
// int taskId = App.Instance.GetModel().CurGuideMissionConfig.Id;
// int remainingTaskCount = _data.Config.TaskIds[0] - taskId + 1;
// remainingTaskCount = Mathf.Max(0, remainingTaskCount);
// tip = string.Format(_data.Config.LockTip, remainingTaskCount);
// }
// Toast.Info(tip);
// return;
// }
// `;
// const snippets = extractor.extractStrings(code);
// {
// let targetSnippet = snippets[0];
// expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[_eventIdx]`));
// expect(targetSnippet.originalCode).toBe(`_table.MainDescription[_eventIdx]`);
// expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[_eventIdx].TR()`);
// expect(targetSnippet.literals).toEqual([
// ]);
// expect(targetSnippet.isChanged).toBe(true);
// }
// {
// let targetSnippet = snippets[1];
// expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[0]`));
// expect(targetSnippet.originalCode).toBe(`_table.MainDescription[0]`);
// expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[0].TR()`);
// expect(targetSnippet.literals).toEqual([
// ]);
// expect(targetSnippet.isChanged).toBe(true);
// }
// expect(snippets.length).toBe(2);
// })
// // 测试if语句无花括号包围的单句表达式语句中的字符串表达式处理
// test('should handle assignment expression within if-else-end', () => {
// const code = `
// protected override void OnCreate()
// {
// base.OnCreate();
// }
// protected override void OnShow(object param)
// {
// base.OnShow(param);
// var mailContent = (MailContentDialogParam)param;
// if (mailContent == null) return;
// var mailData = this.GetModel().GetMail(mailContent.MailId);
// if (mailData == null) return;
// _mailId = mailData.MailId;
// m_text_title.text = mailData.Title.TR();
// m_text_name.text = $"道友如晤:".TR();
// m_text_sender.text = Tr.Format("{0}稽首", mailData.Name);
// //转换时区
// // var dateTimeOffset = TimeZoneInfo.ConvertTime(DateTimeOffset.FromUnixTimeSeconds(mailData.ReceiveTime), TimeZoneInfo.Local);
// m_text_time.text = Date.ConvertTimestampToTimeString(mailData.ReceiveTime, "yyyy年M月d日 HH:mm").TR();
// m_scroll_content.title = mailData.Content;
// `;
// const snippets = extractor.extractStrings(code);
// {
// let targetSnippet = snippets[0];
// expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[_eventIdx]`));
// expect(targetSnippet.originalCode).toBe(`_table.MainDescription[_eventIdx]`);
// expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[_eventIdx].TR()`);
// expect(targetSnippet.literals).toEqual([
// ]);
// expect(targetSnippet.isChanged).toBe(true);
// }
// {
// let targetSnippet = snippets[1];
// expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[0]`));
// expect(targetSnippet.originalCode).toBe(`_table.MainDescription[0]`);
// expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[0].TR()`);
// expect(targetSnippet.literals).toEqual([
// ]);
// expect(targetSnippet.isChanged).toBe(true);
// }
// expect(snippets.length).toBe(2);
// })
// // 测试if语句无花括号包围的单句表达式语句中的字符串表达式处理
// test('should handle assignment expression within if-else-end', () => {
// const code = `
// public void BindData(MailData mailData)
// {
// if (mailData == null) return;
// _mailData = mailData;
// //状态
// m_hasRead.selectedIndex = _mailData.HasRead ? (int)HasRead.True : (int)HasRead.False;
// m_hasFetch.selectedIndex = mailData.Rewards.Count > 0 ? (int)HasFetch.True : (int)HasFetch.False;
// m_text_title.text = _mailData.Title.TR();
// m_text_subTitle.text = _mailData.SubTitle.TR();
// //显示时间
// long interval = DateTimeOffset.Now.ToUnixTimeSeconds() - mailData.ReceiveTime;
// if (interval <= 0) interval = 1;
// if (interval < 60) m_text_time.text = "现在";
// else if (interval < 3600) m_text_time.text = $"{interval / 60}分钟前";
// else if (interval < 86400) m_text_time.text = $"{interval / 3600}小时前";
// else if (interval < 2592000) m_text_time.text = $"{interval / 86400}天前";
// else if (interval < 31104000) m_text_time.text = $"{interval / 2592000}月前";
// else m_text_time.text = Tr.Format("{0}年前", interval / 31104000);
// }
// `;
// const snippets = extractor.extractStrings(code);
// {
// let targetSnippet = snippets[0];
// expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[_eventIdx]`));
// expect(targetSnippet.originalCode).toBe(`_table.MainDescription[_eventIdx]`);
// expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[_eventIdx].TR()`);
// expect(targetSnippet.literals).toEqual([
// ]);
// expect(targetSnippet.isChanged).toBe(true);
// }
// {
// let targetSnippet = snippets[1];
// expect(targetSnippet.originalIndex).toBe(code.indexOf(`_table.MainDescription[0]`));
// expect(targetSnippet.originalCode).toBe(`_table.MainDescription[0]`);
// expect(targetSnippet.convertedCode).toBe(`_table.MainDescription[0].TR()`);
// expect(targetSnippet.literals).toEqual([
// ]);
// expect(targetSnippet.isChanged).toBe(true);
// }
// expect(snippets.length).toBe(2);
// })
});