前面博客介绍了定位、操作页面元素以及如何校验执行结果。此篇博客将介绍如何处理页面弹框、文件上传下载、操作iframe里面页面元素,为了完成课程目标,分了3个task
- 处理页面弹框
- 实现文件上传和下载操作
- 操作iframe中的元素
接下来就开始第一个task的学习,同样执行“npm run handle-dialog”即可运行下面讲解的案例。
处理页面弹框
弹框主要分为三种类型:alertbox、confirmbox、promptbox。alterbox主要用于给用户显示一些提示信息,通常会带一个OK按钮,点击OK按钮即可关闭alterbox。confirmbox主要让用户输入确认或者取消信息,confirmbox通常会带OK和CANCEL按钮,点击OK或者CANCEL都可以关闭confirmbox。promptbox主要让用户输入一些信息,通常会带一个text输入框和OK、CANCEL按钮,点击OK或者CANCEL都可以关闭promptbox。
如下图所示,当点击页面的populate按钮时,页面弹出了alertbox。
如果不关闭上面的弹框,页面上其他element都不可点击。对于这类弹框,处理的机制是实时监听页面上是否显示了弹框,如果有即可关闭,防止其影响正常的自动化脚本运行。那么使用puppeteer框架时如何处理弹框呢?请看下面的案例。同样,执行“npm run handle-dialog”即可运行下面的案例。
describe("dialog demo", () => {
it("simulate click close on alert dialog", async () => {
await page.on('dialog', async dialog => {
switch (dialog.type()) {
case 'alert':
await dialog.dismiss();
break;
case 'confirm':
await dialog.accept();
break;
case 'prompt':
await dialog.accept("type things");
break;
default :
throw "can't get dialog type"
}
//自动化脚本最前面通过page.on()添加对页面的监听操作,如果出现弹框,调用dialog.dismiss()方法关闭弹框
//dialog.dismiss()相当于点击弹框右上角的关闭按钮
//dialog.accept()相当于点击弹框的OK按钮,如果是prompt类型的弹框,调用dialog.accept('conent')模拟在prompt弹框中输入conent
//dialog.type()返回监听到的dialog类型,类型包含:alert,confirm,prompt和beforeunload
});
await page.goto('https://devexpress.github.io/testcafe/example/');
await expect(page).toClick('#populate');
});
it("simulate click ok button on alert dialog", async () => {
await page.goto("https://chercher.tech/practice/popups");
await expect(page).toClick('input[name="alert"]');
});
it("simulate click ok button on confirm dialog", async () => {
await page.goto("https://chercher.tech/practice/popups");
await expect(page).toClick('input[name="confirmation"]');
});
it("simulate input text on promote dialog", async () => {
await page.goto("https://chercher.tech/practice/popups");
await expect(page).toClick('input[name="prompt"]');
})
});
看了上面的案例,你掌握了如何处理dialog框了么?这里我们总结下dialog框常用方法。
- 1.dialog.message()获取dialog框中的message信息,当一个测试流程中可能出现多个dialog框,那么可以获取message信息,然后根据信息进行下一步操作。
- 2.dialog.dismiss()等同于点击dialog框右上角的关闭按钮,如果实际场景中你只是想关闭dialog框,不影响正常的自动化脚本,那么可以用此方法。注意,这里即便弹出的dialog框中右上角没有关闭按钮,仍然可以调用此方法关闭dialog。
- 3.dialog.accept()等同于点击dialog框中的确认按钮,如果方法中传入参数值,那么当监听到prompt弹框时会把accept()中参数值输入到prompt弹框中。
- 4.dialog.defaultValue(),如果是prompt的弹框会返回prompt弹框中的值,如果不是prompt弹框,返回空字符串。
- 5.dialog.type()获取弹框的类型,实际项目中可以根据弹框类型进行不同的处理逻辑。
以上就是关于dialog弹框的所有常用方法,掌握了这些方法,处理dialog弹框不再是难事。接下来我们学习如何利用puppeteer框架实现文件的上传和下载。
实现文件上传操作
Puppeteer框架中已经封装了文件上传方法,在需要文件上传的页面调用该方法即可完成文件上传。同样,执行“npm run upload-file”即可运行下面讲解的案例。案例的代码如下所示
const path = require('path');
// 引入path,供后面使用
describe("upload file demo", () => {
it("should upload file successfully", async () => {
page.goto("https://chercher.tech/practice/popups");
await page.waitForSelector('input[type="file"]');
//添加等待语句,保证上传文件的元素已显示在页面上后,再进行下一步操作
const uploadFileHandle = await page.$('input[type="file"]');
await uploadFileHandle.uploadFile('./testdata/test.txt');
//上传文件,这里文件地址是基于代码根目录的相对地址
await page.waitFor(3000);
//这里调用了包好的一个固定时间等待方法,方便查看文件是否真正已上传成功
});
it("should upload file with puppeteer-expect", async () => {
page.goto("https://chercher.tech/practice/popups");
await page.waitFor(3000);
await expect(page).toUploadFile(
'input[type="file"]',
path.join('./testdata/', 'test.txt'),
);
//puppeteer-expect提供了上传文件的方法,按官方文档提示此方法可以实现自动等待,但经过验证发现,在调用上传文件的方法前需要写入等待语句,否则案例会运行失败
await page.waitFor(3000);
})
});
总结而言,要完成文件上传,首选需要添加等待语句,保证“上传文件”页面元素已显示在web应用页面上,接着调用puppeteer提供的方法或者puppeteer-expect提供的上传文件方法上传即可,传入的文件地址写相对地址即可。这里需要注意一点,真实项目中,如果有上传文件场景,需要把上传的文件和代码放在一起。例如案例中在代码根目录下创建了testdata目录,在此目录存放需要上传的文件。不要放自己电脑的其他目录,否则脚本在持续集成平台上运行时,会因为找不到文件而报错。
上面演示了如何上传文件,接下来我们看看如何实现文件下载。Web应用中往往是点击某个链接、图片等完成下载操作,总的来说都是点击页面某个元素。所以,实现文件下载实际就是点击页面的目标元素,和之前讲过的点击操作相同,唯一的差别是:可以指定下载的文件存放位置。下面是案例代码,同样,执行“npm run download-file”即可运行下面的案例。
describe('download file demo',()=> {
it('should download file successfully', async()=> {
await page.goto('https://file-examples.com/index.php/sample-documents-download/sample-doc-download/');
//打开一个存在文件下载链接的web页面
await page._client.send('Page.setDownloadBehavior', {behavior: 'allow', downloadPath: './'});
//如果无此行代码,下载的文件存放到默认下载路径中,如果添加此语句,可以通过downloadPath指定下载的文件存放路径,这里指定为./,那么下载的文件会存放到代码根目录下
await expect(page).toClick('td a',{text:'Download sample DOC file'});
//点击目标元素,完成下载操作
await page.waitFor(8000);
//等待语句,方便查看是否下载过程
})
});
介绍完文件的上传和下载后,下面介绍如何操作iframe里面的页面元素。
操作iframe中的元素
Iframe上元素的操作实际也很简单,如何使用请看下面的案例,同样执行“npm run iframe-action”即可运行下面的案例。
describe("iframe demo", () => {
it("should control element in iframe successfully", async() => {
page.on('dialog', async dialog => {
await dialog.dismiss()});
//这个场景当下面点击button按钮的时候,会弹出一个dialog框,这里通过添加page.on的方式关闭弹出的dialog框
await page.goto('https://www.w3schools.com/TAgs/tryit.asp?filename=tryhtml_button_test');
//打开页面
const frame = await page.frames().find(f => f.name() === 'iframeResult');
//通过名字获取需要的iframe对象,当一个页面有多个iframe时,可以通过名字匹配到需要的iframe
await frame.waitForSelector('button');
await frame.click('button');
//如果需要获取iframe上的元素,使用frame.$即可,所有在page对象上能调用的方法,iframe对象上也可调用
});
通过上面的案例可以看到,对于在iframe里面的页面元素,首选是获取iframe对象,然后基于iframe对象查找、操作页面元素即可。在page对象上能调用的方法,在iframe上基本都可以使用。
接下来我们再看看iframe和文件上传结合的案例,如下图所示,可以看到该页面上的文件上传元素是在iframe里面。
上面的web页面上,如果要实现上传文件,首先必须先获取iframe对象,如果直接基于page对象调用上传文件方法,无法成功上传文件,但脚本不会报错,因为定位元素的时候,确实存在这个元素。实际项目中,如果你调用方法对页面元素进行操作时始终无法成功,可查看下该元素是否在iframe里面。iframe下实现文件上传案例代码如下所示。
it("should upload file in iframe successfully ", async() => {
await page.goto("https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_type_file");
const elementHandle = await page.$('iframe[name="iframeResult"]');
const frame = await elementHandle.contentFrame();
//获取iframe对象的另外一种写法,两种方式都可以获取到iframe对象
await frame.waitForSelector('input[type="file"]');
const uploadFileHandle = await frame.$('input[type="file"]');
await uploadFileHandle .uploadFile('./testdata/test.txt')
//这个web应用上,上传文件的元素在iframe上,故这里获取iframe对象后,再通过iframe.$方式获取文件上传对象,完成文件上传操作
})
});