Linux CI环境构建Chrome扩展时遇backgroundPage获取超时问题
问题排查与修复方案
核心问题分析
- 无头模式配置冲突:代码同时设置
headless: false和args: ['--headless=new'],导致浏览器启动模式混乱。本地Mac环境可能兼容这种冲突,但Linux CI环境会严格遵循命令行参数,引发扩展加载逻辑异常。 - 硬等待不可靠:
setTimeout(3000)的固定等待时间无法适配CI环境的资源波动,扩展背景页可能未初始化完成就执行后续逻辑。 - 事件监听后未更新变量:调用
waitForEvent('backgroundpage')后,未将返回的新背景页赋值给background变量,后续仍使用初始空值,导致获取extensionId失败。 - 路径与构建完整性:CI环境下扩展构建产物路径可能与本地不一致,或构建过程存在缓存、权限问题,导致加载的扩展不完整。
具体修复步骤
1. 修正无头模式配置
移除headless: false,统一使用--headless=new参数,确保浏览器启动模式一致:
const context = await chromium.launchPersistentContext('', { args: [ '--headless=new', `--disable-extensions-except=${pathToExtension}`, `--load-extension=${pathToExtension}` ] });
2. 替换硬等待为事件驱动逻辑
去掉固定setTimeout,改用waitForEvent直接等待背景页就绪,避免依赖不可靠的时间预估:
// 等待背景页事件触发,直接获取有效页面实例 const background = await context.waitForEvent('backgroundpage', { timeout: 300000 });
3. 优化背景页获取逻辑
直接通过事件监听获取背景页,无需先判断context.backgroundPages()——CI环境下初始调用可能返回空数组:
try { const background = await context.waitForEvent('backgroundpage', { timeout: 300000 }); const extensionId = background.url().split('/')[2]; console.log('Extension ID:', extensionId); testInfo.context = context; testInfo.extensionId = extensionId; } catch (error) { console.error('Error occurred while getting the extension ID:', error); } finally { await context.close(); // 无论成败都关闭上下文,避免资源泄漏 }
4. 验证CI环境下的扩展路径与构建完整性
- 在代码中打印扩展路径,确认CI环境下路径正确:
console.log('Loading extension from:', pathToExtension); - 构建扩展时添加
--clean参数,避免CI缓存旧产物:const command = 'yarn workspace @akeyless/ztwa-extension build:prod --clean'; - 检查CI环境下扩展构建产物的权限,确保Chromium有权限读取目录文件。
5. 添加CI适配参数
针对Linux CI环境,添加Chromium兼容性参数,避免沙箱或资源限制问题:
args: [ '--headless=new', `--disable-extensions-except=${pathToExtension}`, `--load-extension=${pathToExtension}`, '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage' ]
完整修复后的代码示例
import { chromium, BrowserContext } from '@playwright/test'; import path from 'path'; import { automationPath } from 'automation/applications/common/path'; import { execSync } from 'child_process'; interface TestInfo { context: BrowserContext; extensionId: string; } export default async ({}, testInfo: TestInfo) => { testInfo = testInfo || {}; const command = 'yarn workspace @akeyless/ztwa-extension build:prod --clean'; execSync(command, { encoding: 'utf-8', maxBuffer: 1000 * 10000 * 100 }); const pathToExtension = path.join(__dirname, automationPath.extension); console.log('Loading extension from:', pathToExtension); const context = await chromium.launchPersistentContext('', { args: [ '--headless=new', `--disable-extensions-except=${pathToExtension}`, `--load-extension=${pathToExtension}`, '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage' ] }); try { const background = await context.waitForEvent('backgroundpage', { timeout: 300000 }); const extensionId = background.url().split('/')[2]; console.log('Extension ID:', extensionId); testInfo.context = context; testInfo.extensionId = extensionId; } catch (error) { console.error('Error occurred while getting the extension ID:', error); } finally { await context.close(); } };
内容的提问来源于stack exchange,提问作者libi
相关产品推荐
相关产品推荐

