《PlayWright全解析——从入门到精通》-5
处理对话框
默认情况下,PlayWright会自动取消对话框(所有对话框都默认选择“取消”),我们这里所说的对话框包括alert
, confirm
, prompt
。我们可以通过在触发对话框弹出之前,注册处理对话框的方式,看下面的例子:
1 2 |
page.on('dialog', dialog => dialog.accept()); await page.getByRole('button').click(); |
当设置过dialog.accept()
之后,整个page后续任何弹出对话框都会按照accept处理。
但是注意了,必须在处理对话框的函数中,最后调用dialog.accept()
或dialog.dismiss()
,否则对话框因为没有接受到确认或取消事件,会卡住整个页面,看下面的例子,就将出现卡住的情况:
1 2 |
page.on('dialog', dialog => console.log(dialog.message())); await page.getByRole('button').click(); // 会一直卡在这里 |
处理新的弹出页面
对PlayWright来说,页面对应的就是Page对象,当我们点击一个链接弹出一个新页面的时候,可以使用注册一个popup
事件,来处理新窗口。
举个例子,假设页面上链接,文字叫“点我”,点击后会弹出新页面:
1 2 3 4 5 6 7 |
// 注册一个“弹出”的事件,注意这里是一个Promise对象 const newPagePromise = page.waitForEvent('popup'); await page.getByText('点我').click(); const newPage = await newPagePromise; // 等待新页面的弹出. await newPage.waitForLoadState(); console.log(await ponewPagepup.title()); |
如果并不清楚哪个操作会弹出新页面,那么可以事先注册on事件:
1 2 3 4 5 |
// 这里注册的函数,会在后面任何当前Page上弹出新页面时触发 page.on('popup', async popup => { await popup.waitForLoadState(); console.log(await popup.title()); }) |
Frame处理
还记得Selenium中是如何处理页面中的Frame的吗?是使用switch方法来做的。PlayWright的做法和Selenium有点类似,核心都是要先定位到页面中的frame,然后再对frame中的元素进行操作。
PlayWright中有两个和Frame相关的类,一个是Frame,一个是FrameLocator。所谓的Frame,是页面中的一个
或者元素对象,可以理解为一个独立的Page对象。
而FrameLocator则是指向Frame的一个定位器。
分别来看下如何使用:
1 2 3 4 5 6 7 8 |
// 通过frame的name属性定位到frame对象 const frame = page.frame('frame-login'); // 通过iframe引入的url地址定位frame对象 const frame = page.frame({ url: /.*domain.*/ }); // 操作frame对象中的元素 await frame?.getByRole('button', { name: /submit/i }).click(); |
注意,
page.frame()
方法返回的是Frame|null
,所以在使用frame进行后面的操作时,需要用frame?
来判断是否为null。
page.frame()
方法的参数可以是一个字符串,表示frame的name属性,或者是一个对象:
1 2 3 4 |
{ name?: string, // 表示frame的name属性 url?: string|regexp, // 字符串或者正则表达式,表示iframe的url } |
再看看FrameLocator,其实就是混在locator中一起用:
1 2 3 |
// 定位一个在frame中的元素 const username = await page.frameLocator('.frame-class').getByLabel('User Name'); await username.fill('John'); |
page.frameLocator()
方法的参数是一个字符串,和page.locator()
方法的参数一样,通常就是css选择器或者xpath表达式。
一个Locator如果定位正好是一个ifram元素,那么也可以直接转换成FrameLocator:
1
|
const frameLocator = locator.frameLocator(':scope');
|
执行Javascript脚本
在PlayWright中可以使用page.evaluate()
或者locator.evaluate()
方法来执行脚本。执行的是一个匿名函数,或者说是一个闭包,以lambda表达式的形式体现,这个函数的返回值将传递给evaluate
方法,作为这个方法的返回值。
看个简单的例子:
1
|
const href = await page.evaluate(() => document.location.href);
|
还可以传参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// 传数字参数 await page.evaluate(num => num, 42); // 传数字数组. await page.evaluate(array => array.length, [1, 2, 3]); // 传对象 await page.evaluate(object => object.foo, { foo: 'bar' }); // 传elementHandle对象. const button = await page.evaluate('window.button'); await page.evaluate(button => button.textContent, button); // locator.evaluate()的用法 await button.evaluate((button, from) => button.textContent.substring(from), 5); // 传多个elementHandle对象 const button1 = await page.evaluate('window.button1'); const button2 = await page.evaluate('window.button2'); await page.evaluate( o => o.button1.textContent + o.button2.textContent, { button1, button2 }); // 解构多个参数的方式 await page.evaluate( ({ button1, button2 }) => button1.textContent + button2.textContent, { button1, button2 }); // 也可以传对象数组 await page.evaluate( ([b1, b2]) => b1.textContent + b2.textContent, [button1, button2]); // 混合类型的数组 await page.evaluate( x => x.button1.textContent + x.list[0].textContent + String(x.foo), { button1, list: [button2], foo: null }); |