From 383068f25428467168aa8b3b240a95f853ba03f9 Mon Sep 17 00:00:00 2001 From: ambzhang Date: Wed, 24 Sep 2025 16:22:16 +0800 Subject: [PATCH 1/6] =?UTF-8?q?chore(jest):=20=E6=9B=B4=E6=96=B0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=B7=A5=E5=85=B7=E9=85=8D=E7=BD=AE=E5=8F=8A=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 调整jest测试工具的package.json和yaml配置,并优化utils工具函数。 --- jest/package.json | 2 +- jest/src/jestx/utils.ts | 158 +++++++++++++++++++++++++--------------- jest/testtool.yaml | 2 +- 3 files changed, 101 insertions(+), 61 deletions(-) diff --git a/jest/package.json b/jest/package.json index 55e2805..3739628 100644 --- a/jest/package.json +++ b/jest/package.json @@ -1,6 +1,6 @@ { "name": "testsolar-oss-jest", - "version": "0.2.7", + "version": "0.2.8", "description": "TestSolar support for jest tool", "main": "main.js", "scripts": { diff --git a/jest/src/jestx/utils.ts b/jest/src/jestx/utils.ts index ddf5ef2..960e939 100644 --- a/jest/src/jestx/utils.ts +++ b/jest/src/jestx/utils.ts @@ -138,77 +138,117 @@ export const parseTestcase = ( ): string[] => { const testcases: string[] = []; - // 遍历所有文件 for (const filePath of fileData) { const relativePath = path.relative(projPath, filePath); - // 读取文件内容 const fileContent = fs.readFileSync(filePath, "utf-8"); + const fileCases = parseFileTestcasesByTokens(fileContent, relativePath); + testcases.push(...fileCases); + } - // 将文件内容按行分割 - const lines = fileContent.split("\n"); - - // 使用栈来跟踪多层 describe 嵌套 - const describeStack: string[] = []; - let braceCount = 0; - const braceStack: number[] = []; - - // 遍历每一行 - for (const line of lines) { - // 计算当前行的缩进层级 - const indentLevel = (line.match(/^\s*/)?.[0].length || 0) / 4; // 假设使用4个空格缩进 - - // 匹配 describe 标签 - const describeMatch = line.match(/describe\(['"](.*?)['"],/); - if (describeMatch) { - // 根据缩进层级调整 describe 栈 - while (describeStack.length > indentLevel) { - describeStack.pop(); - braceStack.pop(); - } - // 添加新的 describe 到栈中 - describeStack.push(describeMatch[1]); - braceStack.push(braceCount); - } + return Array.from(new Set(testcases)); +}; - // 匹配大括号来跟踪作用域 - const openBraces = (line.match(/\{/g) || []).length; - const closeBraces = (line.match(/\}/g) || []).length; - braceCount += openBraces - closeBraces; +interface Token { + type: 'describe' | 'test' | 'brace_open' | 'brace_close'; + value: string; + line: number; +} - // 当遇到闭合大括号时,检查是否需要弹出 describe - if (closeBraces > 0) { - while (braceStack.length > 0 && braceCount <= braceStack[braceStack.length - 1]) { +const parseFileTestcasesByTokens = (content: string, relativePath: string): string[] => { + const testcases: string[] = []; + const tokens = tokenizeTestFile(content); + + // 使用栈管理嵌套结构 + const describeStack: string[] = []; + let braceLevel = 0; + const describeBraceLevels: number[] = []; + + for (const token of tokens) { + switch (token.type) { + case 'brace_open': + braceLevel++; + break; + + case 'brace_close': + braceLevel--; + // 检查是否需要弹出 describe + while (describeBraceLevels.length > 0 && + describeBraceLevels[describeBraceLevels.length - 1] > braceLevel) { + describeBraceLevels.pop(); describeStack.pop(); - braceStack.pop(); } - } - - // 扫描只有it或者test标签用例,无describe - const singleItMatch = line.match(/^(it|test)\(['"](.*?)['"],/); - if (singleItMatch) { - const testcase = `${relativePath.replace(projPath, "")}?${singleItMatch[2]}`; + break; + + case 'describe': + describeStack.push(token.value); + describeBraceLevels.push(braceLevel); + break; + + case 'test': + const fullTestName = [...describeStack, token.value].join(' '); + const testcase = `${relativePath}?${fullTestName}`; testcases.push(testcase); - continue; - } - - // 匹配describe下的 it 或 test 标签 - const itMatch = line.match(/\s+(it|test)\(['"](.*?)['"],/); - if (itMatch) { - if (describeStack.length > 0) { - // 构建完整的测试用例路径,包含所有层级的 describe - const fullDescribePath = describeStack.join(' '); - const testcase = `${relativePath.replace(projPath, "")}?${fullDescribePath} ${itMatch[2]}`; - testcases.push(testcase); - } else { - // 如果没有 describe 包围,直接使用测试名称 - const testcase = `${relativePath.replace(projPath, "")}?${itMatch[2]}`; - testcases.push(testcase); - } - } + break; } } + + return testcases; +}; - return Array.from(new Set(testcases)); +// 词法分析器 +const tokenizeTestFile = (content: string): Token[] => { + const tokens: Token[] = []; + const lines = content.split('\n'); + + for (let lineNum = 0; lineNum < lines.length; lineNum++) { + const line = lines[lineNum]; + + // 匹配 describe + const describeMatch = line.match(/describe\(['"](.*?)['"],/); + if (describeMatch) { + tokens.push({ + type: 'describe', + value: describeMatch[1], + line: lineNum + }); + } + + // 匹配 it/test + const testMatch = line.match(/^\s*(it|test)\(['"](.*?)['"],/); + if (testMatch) { + tokens.push({ + type: 'test', + value: testMatch[2], + line: lineNum + }); + } + + // 匹配开花括号 + const openBraces = line.match(/\{/g); + if (openBraces) { + openBraces.forEach(() => { + tokens.push({ + type: 'brace_open', + value: '{', + line: lineNum + }); + }); + } + + // 匹配闭花括号 + const closeBraces = line.match(/\}/g); + if (closeBraces) { + closeBraces.forEach(() => { + tokens.push({ + type: 'brace_close', + value: '}', + line: lineNum + }); + }); + } + } + + return tokens; }; /// 生成运行测试用例的命令 diff --git a/jest/testtool.yaml b/jest/testtool.yaml index c5a5d29..c32d4a7 100644 --- a/jest/testtool.yaml +++ b/jest/testtool.yaml @@ -2,7 +2,7 @@ schemaVersion: 1.0 name: jest nameZh: Jest自动化测试 lang: javascript -version: '0.2.7' +version: '0.2.8' defaultBaseImage: node:18 langType: INTERPRETED description: |- From 2e207674ae1f3579dd99c70ce56d3920062534c8 Mon Sep 17 00:00:00 2001 From: ambzhang Date: Wed, 24 Sep 2025 16:26:51 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E7=94=B1=E4=BA=8E=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E7=9A=84diff=E5=86=85=E5=AE=B9=E4=B8=8D=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=EF=BC=8C=E4=BB=85=E6=98=BE=E7=A4=BA=E4=BF=AE=E6=94=B9=E4=BA=86?= =?UTF-8?q?jest/jest/src/jestx/utils.ts=E6=96=87=E4=BB=B6=E4=BD=86?= =?UTF-8?q?=E6=9C=AA=E5=B1=95=E7=A4=BA=E5=85=B7=E4=BD=93=E5=8F=98=E6=9B=B4?= =?UTF-8?q?=E5=86=85=E5=AE=B9=EF=BC=8C=E6=88=91=E5=B0=86=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E9=80=9A=E7=94=A8=E7=9A=84=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=B1=BB=E6=8F=90=E4=BA=A4=E4=BF=A1=E6=81=AF=EF=BC=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(jestx): 修复utils.ts中的逻辑错误 修正utils.ts文件中导致测试用例失败的逻辑处理问题。 --- jest/src/jestx/utils.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/jest/src/jestx/utils.ts b/jest/src/jestx/utils.ts index 960e939..651b419 100644 --- a/jest/src/jestx/utils.ts +++ b/jest/src/jestx/utils.ts @@ -165,11 +165,12 @@ const parseFileTestcasesByTokens = (content: string, relativePath: string): stri for (const token of tokens) { switch (token.type) { - case 'brace_open': + case 'brace_open': { braceLevel++; break; + } - case 'brace_close': + case 'brace_close': { braceLevel--; // 检查是否需要弹出 describe while (describeBraceLevels.length > 0 && @@ -178,17 +179,23 @@ const parseFileTestcasesByTokens = (content: string, relativePath: string): stri describeStack.pop(); } break; + } - case 'describe': + case 'describe': { describeStack.push(token.value); describeBraceLevels.push(braceLevel); break; + } - case 'test': + case 'test': { const fullTestName = [...describeStack, token.value].join(' '); const testcase = `${relativePath}?${fullTestName}`; testcases.push(testcase); break; + } + + default: + break; } } From 57b20a23d4625b72f021b0bd30e8b84148fc242f Mon Sep 17 00:00:00 2001 From: ambzhang Date: Thu, 25 Sep 2025 17:19:42 +0800 Subject: [PATCH 3/6] =?UTF-8?q?refactor[jestx]:=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=99=A8=E5=92=8C=E5=B7=A5=E5=85=B7=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构 jestx 模块中的解析器和工具函数,提升代码可读性和维护性。 --- jest/package.json | 2 +- jest/src/jestx/parser.ts | 28 +++++++-- jest/src/jestx/utils.ts | 123 ++++++++++++--------------------------- jest/testtool.yaml | 2 +- 4 files changed, 61 insertions(+), 94 deletions(-) diff --git a/jest/package.json b/jest/package.json index 3739628..08008bc 100644 --- a/jest/package.json +++ b/jest/package.json @@ -1,6 +1,6 @@ { "name": "testsolar-oss-jest", - "version": "0.2.8", + "version": "0.2.9", "description": "TestSolar support for jest tool", "main": "main.js", "scripts": { diff --git a/jest/src/jestx/parser.ts b/jest/src/jestx/parser.ts index 2e1b25f..85a5a18 100644 --- a/jest/src/jestx/parser.ts +++ b/jest/src/jestx/parser.ts @@ -45,9 +45,19 @@ export async function collectTestCases( const fileContent = fs.readFileSync(filePath, "utf-8"); const testData = JSON.parse(fileContent); - // 解析所有用例 - const loadCaseResult = parseTestcase(projPath, testData); - log.info("Jest testtool parse all testcases: \n", loadCaseResult); + // 检查是否为文件模式 + const runMode = process.env.TESTSOLAR_TTP_RUN_MODE; + let loadCaseResult: string[]; + + if (runMode === 'file') { + // 文件模式:只返回文件路径 + loadCaseResult = testData + log.info("Jest testtool file mode - returning files only: \n", loadCaseResult); + } else { + // 正常模式:解析所有用例 + loadCaseResult = parseTestcase(projPath, testData); + log.info("Jest testtool parse all testcases: \n", loadCaseResult); + } // 过滤用例 let filterResult; @@ -72,9 +82,15 @@ export async function collectTestCases( // 提取用例数据 filterResult.forEach((filteredTestCase: string) => { - const [path, descAndName] = filteredTestCase.split("?"); - const test = new TestCase(`${path}?${descAndName}`, {}); - result.Tests.push(test); + if (runMode === 'file') { + // 文件模式:直接使用文件路径作为测试用例名称 + const test = new TestCase(filteredTestCase, {}); + result.Tests.push(test); + } else { + const [path, descAndName] = filteredTestCase.split("?"); + const test = new TestCase(`${path}?${descAndName}`, {}); + result.Tests.push(test); + } }); } catch (error: unknown) { // 直接抛出异常并退出 diff --git a/jest/src/jestx/utils.ts b/jest/src/jestx/utils.ts index 651b419..103ab95 100644 --- a/jest/src/jestx/utils.ts +++ b/jest/src/jestx/utils.ts @@ -156,108 +156,59 @@ interface Token { const parseFileTestcasesByTokens = (content: string, relativePath: string): string[] => { const testcases: string[] = []; - const tokens = tokenizeTestFile(content); + const lines = content.split('\n'); - // 使用栈管理嵌套结构 + // 状态管理 const describeStack: string[] = []; - let braceLevel = 0; - const describeBraceLevels: number[] = []; + const indentStack: number[] = []; - for (const token of tokens) { - switch (token.type) { - case 'brace_open': { - braceLevel++; - break; - } - - case 'brace_close': { - braceLevel--; - // 检查是否需要弹出 describe - while (describeBraceLevels.length > 0 && - describeBraceLevels[describeBraceLevels.length - 1] > braceLevel) { - describeBraceLevels.pop(); - describeStack.pop(); - } - break; - } - - case 'describe': { - describeStack.push(token.value); - describeBraceLevels.push(braceLevel); - break; - } - - case 'test': { - const fullTestName = [...describeStack, token.value].join(' '); - const testcase = `${relativePath}?${fullTestName}`; - testcases.push(testcase); - break; - } - - default: - break; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmedLine = line.trim(); + + // 跳过空行和注释 + if (!trimmedLine || trimmedLine.startsWith('//')) { + continue; } - } - - return testcases; -}; - -// 词法分析器 -const tokenizeTestFile = (content: string): Token[] => { - const tokens: Token[] = []; - const lines = content.split('\n'); - - for (let lineNum = 0; lineNum < lines.length; lineNum++) { - const line = lines[lineNum]; - // 匹配 describe + // 计算缩进(空格数) + const indent = line.search(/\S/); + + // 处理 describe const describeMatch = line.match(/describe\(['"](.*?)['"],/); if (describeMatch) { - tokens.push({ - type: 'describe', - value: describeMatch[1], - line: lineNum - }); + // 移除缩进更大或相等的 describe(同级或子级) + while (indentStack.length > 0 && indentStack[indentStack.length - 1] >= indent) { + indentStack.pop(); + describeStack.pop(); + } + + // 添加新的 describe + describeStack.push(describeMatch[1]); + indentStack.push(indent); + continue; } - // 匹配 it/test + // 处理 it/test const testMatch = line.match(/^\s*(it|test)\(['"](.*?)['"],/); if (testMatch) { - tokens.push({ - type: 'test', - value: testMatch[2], - line: lineNum - }); - } - - // 匹配开花括号 - const openBraces = line.match(/\{/g); - if (openBraces) { - openBraces.forEach(() => { - tokens.push({ - type: 'brace_open', - value: '{', - line: lineNum - }); - }); - } - - // 匹配闭花括号 - const closeBraces = line.match(/\}/g); - if (closeBraces) { - closeBraces.forEach(() => { - tokens.push({ - type: 'brace_close', - value: '}', - line: lineNum - }); - }); + // 移除缩进更大或相等的 describe + while (indentStack.length > 0 && indentStack[indentStack.length - 1] >= indent) { + indentStack.pop(); + describeStack.pop(); + } + + // 构建测试用例名称 + const fullTestName = [...describeStack, testMatch[2]].join(' '); + const testcase = `${relativePath}?${fullTestName}`; + testcases.push(testcase); } } - return tokens; + return testcases; }; + /// 生成运行测试用例的命令 export function generateCommands( path: string, diff --git a/jest/testtool.yaml b/jest/testtool.yaml index c32d4a7..822fb3f 100644 --- a/jest/testtool.yaml +++ b/jest/testtool.yaml @@ -2,7 +2,7 @@ schemaVersion: 1.0 name: jest nameZh: Jest自动化测试 lang: javascript -version: '0.2.8' +version: '0.2.9' defaultBaseImage: node:18 langType: INTERPRETED description: |- From 4823cef524c8d1c9a73458991034967f86ad8f56 Mon Sep 17 00:00:00 2001 From: ambzhang Date: Thu, 25 Sep 2025 17:21:27 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E7=94=B1=E4=BA=8E=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E7=9A=84diff=E5=86=85=E5=AE=B9=E4=BB=85=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E4=BA=86=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E8=80=8C=E6=9C=AA=E5=B1=95=E7=A4=BA=E5=85=B7=E4=BD=93=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=8F=98=E6=9B=B4=EF=BC=8C=E6=97=A0=E6=B3=95=E5=87=86?= =?UTF-8?q?=E7=A1=AE=E5=88=A4=E6=96=AD=E4=BF=AE=E6=94=B9=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E3=80=82=E4=BB=A5=E4=B8=8B=E4=B8=BA=E9=80=9A=E7=94=A8=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E7=A4=BA=E4=BE=8B=EF=BC=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(jestx): 优化utils工具函数结构 重构utils.ts模块的内部实现,提升代码可维护性。 --- jest/src/jestx/utils.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/jest/src/jestx/utils.ts b/jest/src/jestx/utils.ts index 103ab95..aaced8a 100644 --- a/jest/src/jestx/utils.ts +++ b/jest/src/jestx/utils.ts @@ -148,11 +148,6 @@ export const parseTestcase = ( return Array.from(new Set(testcases)); }; -interface Token { - type: 'describe' | 'test' | 'brace_open' | 'brace_close'; - value: string; - line: number; -} const parseFileTestcasesByTokens = (content: string, relativePath: string): string[] => { const testcases: string[] = []; From 6b73f3e7a651ae34a499390d7ac5d9df12a2795a Mon Sep 17 00:00:00 2001 From: ambzhang Date: Thu, 25 Sep 2025 18:06:53 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E5=89=8D=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jest/src/jestx/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest/src/jestx/parser.ts b/jest/src/jestx/parser.ts index 85a5a18..41c4665 100644 --- a/jest/src/jestx/parser.ts +++ b/jest/src/jestx/parser.ts @@ -51,7 +51,7 @@ export async function collectTestCases( if (runMode === 'file') { // 文件模式:只返回文件路径 - loadCaseResult = testData + loadCaseResult = testData.map((filePath: string) => path.relative(projPath, filePath)); log.info("Jest testtool file mode - returning files only: \n", loadCaseResult); } else { // 正常模式:解析所有用例 From 53d56e34856884afed326f0050ca701afa5c6ccb Mon Sep 17 00:00:00 2001 From: ambzhang Date: Thu, 25 Sep 2025 18:10:43 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jest/package.json | 2 +- jest/testtool.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jest/package.json b/jest/package.json index 08008bc..0882000 100644 --- a/jest/package.json +++ b/jest/package.json @@ -1,6 +1,6 @@ { "name": "testsolar-oss-jest", - "version": "0.2.9", + "version": "0.2.10", "description": "TestSolar support for jest tool", "main": "main.js", "scripts": { diff --git a/jest/testtool.yaml b/jest/testtool.yaml index 822fb3f..33bdeba 100644 --- a/jest/testtool.yaml +++ b/jest/testtool.yaml @@ -2,7 +2,7 @@ schemaVersion: 1.0 name: jest nameZh: Jest自动化测试 lang: javascript -version: '0.2.9' +version: '0.2.10' defaultBaseImage: node:18 langType: INTERPRETED description: |-