我們連續練習了許多操作網站的常見行為,接著,我們要來學習如何處理多分頁/視窗,在網站上點擊一個連結,如果這個連結有 target="_blank"
屬性,會開啟一個新 tab,或者是點擊某個元素,元素標籤有 onclick="openWindow()"
事件,則會開啟新視窗,這些事件在 Playwright 都能夠監聽與處理。
在 Playwright 中,事件的監聽範圍可以分為兩個層級:
context
- 監聽整個瀏覽器情境(Browser Context),能捕捉所有分頁的事件。page
- 僅監聽特定分頁(Page),只會處理該分頁自身觸發的事件。相信各位玩家對於 page
都不陌生,前面我們練習使用時,必須先在宣告 test 時引入,context
也相同,引入 context
或 page
後,我們就可以讓它們來操作整個瀏覽器或特定分頁:
test('context test', async ({ context }) => {
// 測試內容...
})
test('page test', async ({ page }) => {
// 測試內容...
})
測試中點擊按鈕開啟新分頁或新視窗,可以用 popup
事件捕捉:
// 先開始監聽 popup 事件
const popupPromise = page.waitForEvent('popup');
// 觸發會開啟新視窗的點擊
await page.locator().click();
// 等待 popup 事件發生,取得新視窗
const newWindow = await popupPromise;
我們已經掌握了多分頁與視窗的處理技巧,接下來,就一起迎接今天的新任務吧!
難易度 ★★☆☆☆
任務畫面
任務解析
必須先開啟 3 個分頁並前往不同網址,接著關閉第 1 個分頁。並且確認最後保留的是第 2 及第 3 個分頁。
要達成這個任務,我們先宣告三個分頁,接著就能輕鬆操作它們啦 ~
test('open new tab', async ({ context }) => {
// 建立三個分頁
const pageOne = await context.newPage();
const pageTwo = await context.newPage();
const pageThree = await context.newPage();
const url = ['https://www.youtube.com', 'https://www.google.com', 'https://www.facebook.com'];
// 各自前往目標頁面
await pageOne.goto(url[0]);
await pageTwo.goto(url[1]);
await pageThree.goto(url[2]);
// 關閉第 1 個分頁
await pageOne.close();
// 驗證目前開啟的分頁數量
const allPages = context.pages();
await expect(allPages).toHaveLength(2);
// 驗證分頁 URL 且 列出所有分頁的 URL
await allPages.forEach(p => {
expect(p.url()).toContain(url[allPages.indexOf(p) + 1]);
console.log(p.url());
});
});
讓我們看一下執行結果:
可以看到最後列出的網址是第 2 個分頁與第 3 個分頁前往的目標網址,成功通過任務!
難易度 ★★☆☆☆
任務畫面
任務解析
這個任務必須在iT邦幫忙點擊具有 target="_blank"
屬性的 「2025 鐵人賽」 連結,開啟新分頁,並在新分頁中從團隊列表找到 「兩貓一犬一條龍小隊」 並進入團隊戰報。
因為我們是由特定頁面點擊元素後,開啟新分頁,因此,我們不監聽整個瀏覽器,而是以 page
監聽特定分頁,接著設置 popup
事件捕捉新的分頁,然後在新分頁操作完成此次任務。
test('open new tab', async ({ page }) => {
// 前往 iT邦幫忙 首頁
await page.goto('https://ithelp.ithome.com.tw/');
// 監聽新分頁事件,並點擊「2025 鐵人賽」連結
const newTabPromise = page.waitForEvent('popup');
await page.getByRole('link', { name: '2025 鐵人賽' }).click();
const newTab = await newTabPromise;
// 在新分頁中進行操作
await newTab.getByRole('link', { name: '團隊列表' }).click();
await newTab.waitForLoadState('load');
// 點擊「兩貓一犬一條龍小隊」連結,並驗證頁面內容
await newTab.getByRole('link', { name: '兩貓一犬一條龍小隊' }).click();
await expect(newTab.locator('div[class="kv-content"]')).toHaveText('團隊戰報');
});
執行一下看看結果:
我們可以看到 Playwright 成功地操作新分頁,並且順利找到了目標元素,而在 report 當中可以看見,如果在 playwright.config.ts
裡設定 screenshot: 'on'
與 video: 'on'
,會分別記錄下兩個分頁的執行狀況,方便觀察實際執行結果畫面與流程。
難易度 ★★☆☆☆
任務畫面
任務解析
前往 Rahul Shetty’s Automation Practice Page 這個頁面,點擊有 onclick="openWindow()"
事件的「open window」按鈕,會開啟一個新的視窗,並在新視窗中找到黃色的「Access all our Courses」。
test('open new window', async ({ page }) => {
// 前往目標頁面
await page.goto('https://rahulshettyacademy.com/AutomationPractice/');
// 開始監聽 popup 事件,點擊會觸發事件的按鈕,取得新視窗
const popupPromise = page.waitForEvent('popup');
await page.getByRole('button', { name: 'Open Window' }).click();
const newWindow = await popupPromise;
// 等待新視窗載入完成
await newWindow.waitForLoadState('load');
// 在新視窗中找到目標元素
await expect(newWindow.getByRole('button', { name: 'Access all our Courses' })).toBeVisible();
});
執行上面的程式碼,結果 failed 了,Playwright Error Message 顯示找不到元素:
讓我們打開 devtool 確認一下元素:
原來這個元素不是 <button>
,而是 <a>
標籤,讓我們把最後一行替換一下:
await expect(newWindow.getByRole('link', { name: 'Access all our Courses' })).toBeVisible();
再次執行,就能在新頁面順利找到元素啦!
💡 Tips:
眼見不一定為實!有時候畫面上看似「按鈕」的元素,其實在程式碼裡是一個連結。若在測試前沒有確認清楚元素的標籤與屬性,往往就會踩到坑。唯有先確定元素的真實結構,才能讓測試如預期順利執行。
到這裡,我們已經能夠自如地操作新分頁與新視窗。無論是透過 context
管理多個分頁,或是利用 popup
事件監聽 page
開啟的新分頁/視窗,都能在多變的測試情境中靈活應對,並累積更多實用技巧。接下來,我們將進一步學習如何導入 POM(Page Object Model),透過結構化設計減少重複程式碼,不僅讓測試流程更輕鬆高效,也能大幅提升可讀性與維護性。