Protractor通过Selenium Server远程运行时Chrome下载文件验证问题
我明白你遇到的困扰——本地跑Protractor时配置Chrome的下载偏好就能轻松验证文件,但切换到远程Selenium Server后就失效了。核心原因其实很直白:本地配置的下载路径是相对于你运行Protractor的机器的,但远程执行时,Chrome是在Selenium Server所在的机器上运行,下载文件会存在那台机器的对应路径里,而不是你的本地机器,所以你本地的Protractor脚本自然找不到文件。
下面是具体的解决步骤,一步步帮你搞定:
1. 修正ChromeOptions的下载路径配置
首先,你需要把下载路径改成Selenium Server所在机器上的绝对路径,并且要确保该路径已经提前创建、Selenium进程有读写权限。同时建议加上directory_upgrade: true,避免路径不存在时Chrome直接报错。
修改你的Protractor配置文件(比如protractor.conf.js)中的capabilities部分:
capabilities: { browserName: 'chrome', 'goog:chromeOptions': { prefs: { 'download': { 'prompt_for_download': false, 'default_directory': '/tmp/protractor_downloads', // 替换成远程机器的绝对路径 'directory_upgrade': true } }, args: ['--no-sandbox', '--disable-dev-shm-usage'] // 远程环境常用的Chrome启动参数,避免权限问题 } }
提示:比如可以在远程机器上创建
/tmp/protractor_downloads目录,执行mkdir -p /tmp/protractor_downloads && chmod 777 /tmp/protractor_downloads确保权限足够。
2. 验证文件存在的两种实用方式
方式一:直接远程检查文件(适合能SSH访问服务器的场景)
如果你的测试环境能通过SSH访问Selenium Server机器,可以在脚本中调用SSH命令检查文件是否存在。用Node.js的ssh2库就能轻松实现:
const Client = require('ssh2').Client; // 封装远程文件检查函数 async function checkFileOnRemote(serverConfig, filePath) { return new Promise((resolve, reject) => { const conn = new Client(); conn.on('ready', () => { conn.exec(`ls ${filePath}`, (err, stream) => { if (err) reject(err); stream.on('close', (code) => { conn.end(); resolve(code === 0); // 退出码为0表示文件存在 }); }); }).connect(serverConfig); }); } // 在测试用例中调用 it('should verify file download on remote server', async () => { await element(by.id('download-button')).click(); // 根据文件大小调整等待时间,或者用更智能的等待逻辑 await browser.sleep(6000); const fileExists = await checkFileOnRemote({ host: '你的远程服务器IP', username: 'SSH用户名', password: 'SSH密码' }, '/tmp/protractor_downloads/目标文件名.pdf'); expect(fileExists).toBe(true); });
方式二:通过Chrome DevTools Protocol(CDP)获取下载文件(适合无法SSH访问的场景)
如果没法直接访问远程机器,可以利用Chrome的DevTools Protocol监听下载事件,直接获取文件信息甚至内容,完全不需要依赖远程文件系统:
it('should verify download via Chrome DevTools', async () => { // 启用CDP会话和下载监听 const cdpSession = await browser.getDriver().createCDPSession(); await cdpSession.send('Page.enable'); await cdpSession.send('Downloads.enable'); let downloadDone = false; let targetDownloadId = null; // 监听下载完成事件 cdpSession.on('Downloads.downloadCompleted', (event) => { downloadDone = true; targetDownloadId = event.downloadId; }); // 触发下载 await element(by.id('download-button')).click(); // 等待下载完成,超时时间设为10秒可按需调整 await browser.wait(() => downloadDone, 10000, '下载超时未完成'); // 获取下载文件的详细信息 const downloadedFiles = await cdpSession.send('Downloads.getDownloadedFiles', { downloadId: targetDownloadId }); // 验证文件信息,比如文件名、大小 expect(downloadedFiles[0].fileName).toBe('目标文件名.pdf'); expect(downloadedFiles[0].fileSize).toBeGreaterThan(0); });
3. 额外避坑提示
- 如果用的是Selenium Grid集群,要确保所有节点机器上都配置了相同的下载路径,或者动态给每个节点设置对应的路径。
- 绝对不要用相对路径,远程环境下相对路径的上下文是Selenium Server的启动目录,很容易出现找不到路径的问题。
- 对于大文件,尽量用监听网络请求完成的逻辑替代固定的
sleep,比如通过CDP监听网络请求的结束事件,这样更可靠。
内容的提问来源于stack exchange,提问作者Dmytro Sotnikov

