如何从Webdriver.io脚本中提取含嵌套属性的capabilities.browserName?
解决Webdriver.io脚本中嵌套Capabilities的浏览器名称提取问题
问题场景
提取Webdriver.io脚本里的capabilities.browserName时,现有正则在capabilities包含嵌套对象(如proxy)的情况下失效,只能提取到最后一个浏览器名称(如firefox),无法正确处理带嵌套结构的capabilities。
示例Webdriver.io脚本:
const { remote } = require('webdriverio'); var assert = require('assert'); ;(async () => { const browser = await multiremote({ capabilities: { browserName: 'chrome', }, capabilities: { browserName: 'firefox' } }) await browser.url('https://www.google.com'); var title = await browser.getTitle(); assert.equal(title, 'Google'); await browser.deleteSession() })()
嵌套结构的capabilities示例:
capabilities: { browserName: 'chrome', proxy: { proxyType: 'PAC', proxyAutoconfigUrl: 'http://localhost:8888', } },
现有代码问题分析
原提取代码中的正则/\scapabilities\s*?:\s*?\{[^}]+\}/gi存在缺陷:[^}]+会匹配任意非}的字符,遇到嵌套对象的}时会提前终止匹配,导致只能捕获到嵌套对象的第一层内容,无法完整提取整个capabilities对象,最终JSON解析失败或数据缺失。
原提取代码:
var browsers = str.match(/\scapabilities\s*?:\s*?\{[^}]+\}/gi); // find capabilities console.log(browsers.length); // check length for(let i=0; i < browsers.length; i++){ let b = browsers[i].replace(/\scapabilities\s*?:\s*?/, '') // remove capabilities: part .replace(/'/g, '"' ) // convert single quote to double quote .replace(/\s/g, '') // remove spaces, new lines, tabs etc. .replace(/(\w+:)|(\w+ :)/g, function(matchedStr) { // wrap words in double quote as keys are not wrapped return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":'; }); let browser = JSON.parse(b); // parse the string to convert to object console.log(browser.browserName); // Success !!! }
解决方案
方案1:改进正则(支持嵌套花括号匹配)
使用支持递归匹配花括号的正则,完整捕获包含嵌套结构的capabilities对象。修改后的正则可以正确匹配多层嵌套的{}结构。
修改后的提取代码:
// 匹配包含嵌套花括号的capabilities块 var browsers = str.match(/\scapabilities\s*?:\s*?(\{(?:[^{}]+|(?1))*\})/gi); console.log(browsers.length); for(let i=0; i < browsers.length; i++){ let b = browsers[i] .replace(/\scapabilities\s*?:\s*?/, '') .replace(/'/g, '"') // 单引号转双引号 .replace(/(\w+)(?=\s*:)/g, '"$1"'); // 给未加引号的key添加双引号 try { let browser = JSON.parse(b); console.log(browser.browserName); } catch (e) { console.error('解析失败:', e); } }
方案2:使用JS AST解析(更可靠)
对于复杂的JS脚本结构,正则容易出现边界情况,推荐用AST解析工具(如acorn)直接解析脚本的抽象语法树,精准提取multiremote参数中的所有capabilities对象。
步骤:
- 安装依赖:
npm install acorn - 使用以下代码解析:
const acorn = require('acorn'); // 解析脚本字符串为AST const ast = acorn.parse(str, { ecmaVersion: 'latest' }); // 遍历AST找到multiremote的调用 function findMultiremoteCapabilities(node) { if (node.type === 'CallExpression' && node.callee.name === 'multiremote') { const args = node.arguments[0]; if (args.type === 'ObjectExpression') { // 提取所有capabilities属性 return args.properties .filter(prop => prop.key.name === 'capabilities') .map(prop => { // 将AST节点转换为对象(复杂场景可使用acorn-walk或escodegen优化) const obj = {}; prop.value.properties.forEach(p => { obj[p.key.name] = p.value.type === 'Literal' ? p.value.value : null; // 递归处理嵌套对象 if (p.value.type === 'ObjectExpression') { const nestedObj = {}; p.value.properties.forEach(np => { nestedObj[np.key.name] = np.value.value; }); obj[p.key.name] = nestedObj; } }); return obj; }); } } // 递归遍历子节点 for (const key in node) { if (node[key] && typeof node[key] === 'object') { const result = findMultiremoteCapabilities(node[key]); if (result) return result; } } return null; } const capabilitiesList = findMultiremoteCapabilities(ast); capabilitiesList.forEach(cap => console.log(cap.browserName));
说明
- 方案1适合结构相对简单的脚本,修改成本低;
- 方案2更稳定,能处理任意复杂的JS结构,适合大规模或复杂脚本的提取需求。
内容的提问来源于stack exchange,提问作者Aamir Mahmood
相关产品推荐
相关产品推荐

