Protractor:验证新标签页中PDF文档与JSON内容的方法
解决Protractor测试新标签页PDF/JSON验证及同步错误问题
首先咱们先搞定那个同步错误——这个问题太常见了,因为你新打开的PDF页面根本不是Angular应用,而Protractor默认会一直等Angular加载完成,自然就报出同步失败的错误了。然后再来说怎么验证页面是PDF还是JSON。
一、先修复页面同步错误
切换到新窗口后,第一时间禁用Protractor的Angular同步机制,这样它就不会再傻等Angular初始化了:
browser.switchTo().window(newWindowHandle).then(() => { browser.waitForAngularEnabled(false); // 关键一步:告诉Protractor这是非Angular页面 });
如果你的项目支持async/await,代码会清爽很多,后面的示例我都用这种写法,可读性更高。
二、验证页面是PDF还是JSON的几种方法
PDF在浏览器里是特殊渲染的,没法像普通网页那样定位DOM元素,所以得换思路:
方法1:检查响应头的Content-Type(最可靠)
不管是PDF还是JSON,服务器返回的响应头里都会有Content-Type字段——PDF对应application/pdf,JSON对应application/json。你可以先拿到新页面的URL,然后发个HEAD请求查看响应头:
首先得装个HTTP请求库,比如request-promise:
npm install request-promise --save-dev
然后在测试里使用:
const request = require('request-promise'); it('验证点击按钮后打开PDF而非JSON', async () => { // 记录原窗口句柄 const originalHandle = await browser.getWindowHandle(); // 点击按钮 await element(by.id('MyButton')).click(); // 等待新窗口出现,避免直接取handles[1]可能出错 await browser.wait(async () => { const handles = await browser.getAllWindowHandles(); return handles.length === 2; }, 5000, '超时:新窗口未打开'); // 切换到新窗口并禁用Angular同步 const newWindowHandle = (await browser.getAllWindowHandles()).find(h => h !== originalHandle); await browser.switchTo().window(newWindowHandle); await browser.waitForAngularEnabled(false); // 获取当前页面URL并检查响应头 const pageUrl = await browser.getCurrentUrl(); const response = await request.head(pageUrl); const contentType = response.headers['content-type']; // 断言页面是PDF expect(contentType).toContain('application/pdf'); // 切回原窗口,恢复Angular同步,不影响后续测试 await browser.switchTo().window(originalHandle); await browser.waitForAngularEnabled(true); });
方法2:尝试解析页面内容(适合JSON错误场景)
如果测试失败时页面显示JSON,浏览器会直接把JSON文本展示在页面里;而PDF页面你拿不到可解析的文本。所以可以尝试获取页面body文本,看能不能解析成JSON:
it('验证点击按钮后打开PDF而非JSON', async () => { // 前面的步骤和上面一致:获取原句柄、点击按钮、切换新窗口、禁用同步 try { const bodyText = await element(by.tagName('body')).getText(); // 尝试解析成JSON,如果成功说明是错误页 JSON.parse(bodyText); fail('页面显示了JSON,测试失败'); } catch (e) { // 解析失败,说明是PDF,测试通过 expect(true).toBe(true); } // 切回原窗口恢复同步 await browser.switchTo().window(originalHandle); await browser.waitForAngularEnabled(true); });
方法3:检查URL后缀(简单但不够可靠)
如果你的PDF URL固定带.pdf后缀,那直接断言URL包含这个后缀就行:
const pageUrl = await browser.getCurrentUrl(); expect(pageUrl).toContain('.pdf');
不过这个方法有局限性,如果是动态生成的PDF(比如URL是/generate-pdf?id=123)就没用了。
三、优化你的原代码
你原来的嵌套then太绕了,用async/await重构后会清晰很多,而且不容易出错:
it('点击按钮打开PDF新标签页', async () => { const originalHandle = await browser.getWindowHandle(); // 点击按钮 await element(by.id('MyButton')).click(); // 等待新窗口出现 await browser.wait(async () => { const handles = await browser.getAllWindowHandles(); return handles.length === 2; }, 5000, '超时:新窗口未打开'); const handles = await browser.getAllWindowHandles(); const newWindowHandle = handles.find(handle => handle !== originalHandle); // 切换到新窗口并禁用Angular同步 await browser.switchTo().window(newWindowHandle); await browser.waitForAngularEnabled(false); // 这里放入你选择的验证逻辑(比如检查Content-Type) // 切回原窗口,恢复Angular同步 await browser.switchTo().window(originalHandle); await browser.waitForAngularEnabled(true); });
一些注意事项
- 一定要记得切回原窗口后重新启用Angular同步,不然后面的Angular页面测试会出问题。
- 如果你的PDF是通过POST请求生成的,HEAD请求的方法可能无效(因为HEAD没法携带POST参数),这时候可以考虑用浏览器的Performance API监听网络请求来获取响应头。
- 不同浏览器的PDF查看器DOM结构不一样,别想着去定位PDF里的元素,咱们从响应头或者页面内容类型入手更靠谱。
内容的提问来源于stack exchange,提问作者jissay
相关产品推荐
相关产品推荐

