playwright ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ฌ ์ผ์ต๋๋ค. ๊ธ์ ์ด ์์ ์ version์ 1.13.0
/ ์ธ์ด ๋ฐ ํ๊ฒฝ์ Node.js
์
๋๋ค.
๋ค๋ฅธ ์ธ์ด๋ฅผ ์ฐธ์กฐ ํ์๋ ค๋ฉด https://playwright.dev/ ์์ ์ธ์ด๋ฅผ ์ ํํ์๋ฉด ๋ฉ๋๋ค.
Playwright ๋ ํ ์คํธ ํ๋ ์ ์ํฌ? ํ ์คํธ ๋ฌ๋? BDD Tool?
Playwright๋ Playwright Library๋ผ๊ณ ๋ ๋ถ๋ฆฌ๊ธฐ๋ ํ๋๋ฐ browser automation ํด๋ก์จ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์ ์ผ๋ก ๋ธ๋ผ์ฐ์ ๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํ ๊ฒ์ ๋๋ค.
๊ทธ๋ฌ๋๊น Playwright๋ ์ฌ์ค์ ์ฐ๋ฆฌ์ ํ ์คํธ ๋๋ ํ ์คํธ ๋ฌ๋์ ์ฌ๋ถ์ ๋ํด์ ๋ชจ๋ฆ ๋๋ค. (์คํจ, ์ฑ๊ณตํ๋์ง ๋ชจ๋ฆ) ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํ ์คํธ ์คํฌ๋ฆฝํธ์ ๊ฒฐ๊ณผ์ ๋ํ report ๋ํ ์ง์๋์ง ์์ต๋๋ค.
ํ์ง๋ง Playwright์ ๊ธฐ๋ณธ์ ์ฒ ํ์ ๊ทผ๊ฑฐํ E2E ํ ์คํ ์ ์ํ ํด๋ก์จ Playwright Test๋ผ๋ ํ ์คํธ ๋ฌ๋๋ฅผ ์ง์ํ๋ฉฐ ๋ค๋ฅธ ์ธ๋ถ์ ์๋ฐ์คํฌ๋ฆฝํธ ํ ์คํธ ๋ฌ๋์ ํจ๊ป ์ฌ์ฉ์ด ๊ฐ๋ฅํฉ๋๋ค.
Playwright์ ํจ๊ป ์ฌ์ฉ ๊ฐ๋ฅํ ์๋ฐ์คํฌ๋ฆฝํธ ํ ์คํธ ๋ฌ๋/ํ๋ ์์ํฌ :
์์ธํ ์ฌํญ ์ฐธ์กฐ : https://playwright.dev/docs/test-runners/
ํ ์คํธ ํ๊ฒฝ์ค์ ์ ํ๊ธฐ ์ ๋ช๊ฐ์ง ์ฃผ์ ์ปจ์ ์ ์๋ฉด ์ข๊ฒ ๋ค ์ถ์์ต๋๋ค. (ํ ์คํธ ํ๊ฒฝ ์ค์ ์ ๋ค์ ํฌ์คํ ์ผ๋ก ์ฌ๋ฆฌ๊ฒ ์ต๋๋ค.)
Core concept
1.Browser
๋ธ๋ผ์ฐ์ ๋ Chromium, Firefox or WebKit ์ ์ธ์คํด์ค๋ฅผ ๊ฐ๋ฆฌํต๋๋ค. Playwright ์คํฌ๋ฆฝํธ๋ ์ผ๋ฐ์ ์ผ๋ก ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค๋ฅผ ์์ํ๋ ๊ฒ์ผ๋ก ๋ธ๋ผ์ฐ์ ๋ฅผ ๋ซ๋ ๊ฒ์ผ๋ก ๋๋ฉ๋๋ค. ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค๋ ํค๋๋ฆฌ์ค(GUI ์์ด) ๋๋ ํค๋ ๋ชจ๋๋ก ์์ํ ์ ์์ต๋๋ค.
๋งค ํ ์คํธ๋ง๋ค ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค๋ฅผ ์๋ก ์ด๊ธฐํ ํ๋ ๊ฒ์ ๋น์ฉ์ด ๋ง์ด ๋ค๊ธฐ ๋๋ฌธ์ (>100ms), Playwright๋ ํ๋์ ์ธ์คํด์ค๊ฐ ์ฌ๋ฌ๊ฐ์ browser contexts๋ฅผ ํตํด์ ํ ์ ์๋ ์์ ์ ์ต๋ํ ์์ผฐ์ต๋๋ค.
const { chromium } = require('playwright') // Or 'firefox' or 'webkit'.
const browser = await chromium.launch({ headless: false })
await browser.close()
2. BrowserContexts
Browser์์ ์ธ๊ธํ์๋ฏ์ด, ์ธ์คํด์ค ์ด๊ธฐํ ๋น์ฉ์ ์กฐ๊ธ ๋ ์ ๋ ดํ๊ฒ ์ฌ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ด Borwser Context ๋ฅผ ์ด์ฉํ๋ ๊ฒ์ ๋๋ค.
๋์ผํ ์ธ์คํด์ค์์ ๊ฐ์ ๋ค๋ฅธ ๊ฐ๋ณ ์ธ์ ์ผ๋ก ๋ถ๋ฆฌ ํ์ฌ, ์ฌ๋ฌ๊ฐ์ ๋ ๋ฆฝ์ ์ธ ๋ธ๋ผ์ฐ์ ์ธ์ ์ ์ด์ํ๋๋ก ํ๋ ๊ฒ์ ๋๋ค. Playwright์์๋ ํ ์คํธ ๊ฐ์ ๋ธ๋ผ์ฐ์ ์ํ๊ฐ ๋ ๋ฆฝ์ ์ผ๋ก ๊ฒฉ๋ฆฌ ๋๋๋ก, ๊ฐ ํ ์คํธ ์๋๋ฆฌ์ค๋ฅผ ๊ณ ์ ํ ์ ๋ธ๋ผ์ฐ์ ์ปจํ ์คํธ์์ ์คํํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณต๋์ด์ง๋ default browser context๋ ๋ธ๋ผ์ฐ์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ์ฆ์ ์์ฑ๋๋ฉฐ, ์ถ๊ฐ์ ์ผ๋ก ๋ ํ์ํ ๋ธ๋ผ์ฐ์ ์ปจํ ์คํธ๋ ๋ง๋ค ์ ์์ต๋๋ค.
//multiple contexts
const { chromium } = require('playwright')
// Create a Chromium browser instance
const browser = await chromium.launch({ headless: false })
// Create two isolated browser contexts
const userContext = await browser.newContext()
const adminContext = await browser.newContext()
3. Pages
Page๋ ์ฝ๊ฒ ์ด์ผ๊ธฐ ํ์๋ฉด ์น๋ธ๋ผ์ฐ์ ์์ ๊ฐ๊ฐ์ tab ๋๋ ํ์ ์ ๊ฐ๋ฆฌํค๋ฉฐ, Browser context๋ ์ฌ๋ฌ๊ฐ์ ํ์ด์ง๋ฅผ ๊ฐ์ง ์ ์์ต๋๋ค. ์ํ๋ url๋ก ์ด๋ํ๊ณ ํ์ด์ง ์ปจํ ์ธ ์ ์ํธ์์ฉํ๋๋ฐ ์ด์ฉ๋ฉ๋๋ค.
// Create a page.
const page = await context.newPage()
// Navigate explicitly, similar to entering a URL in the browser.
await page.goto('http://example.com')
// Fill an input.
await page.fill('#search', 'query')
// Navigate implicitly by clicking a link.
await page.click('#submit')
// Expect a new url.
console.log(page.url())
// Page can navigate from the script - this will be picked up by Playwright.
window.location.href = 'https://example.com'
4. Selectors
ํ ์คํธํ ์์๋ฅผ ์ ํํ๋ ๋ฐฉ๋ฒ๋ค์ ๋๋ค.
Playwright๋ CSS selectors, XPath selectors, id
,data-test-id
์ ๊ฐ์ HTML attribute ๋ฐ text content
๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒ์ํ ์ ์์ต๋๋ค.
// Using data-test-id= selector engine
await page.click('data-test-id=foo')
// CSS and XPath selector engines are automatically detected
await page.click('div')
await page.click('//html/body/div')
// Find node by text substring
await page.click('text=Hello w')
// Click an element with text 'Sign Up' inside of a #free-month-promo.
await page.click('#free-month-promo >> text=Sign Up')
5. Auto-waiting
ํ์ด์ง ๋ด์ html์์์ ์ํธ์์ฉ์ ํด์ผํ๋ ์ผ๋ค ์ค ์๋ฅผ ๋ค์ด ๋ก๊ทธ์ธ ๋ฒํผ์ ํด๋ฆญ์ ํด์ผํ๋ ๊ฒฝ์ฐ ๋ธ๋ผ์ฐ์ ์ ๋ก๊ทธ์ธ ํ๋ฉด์์ ๋ฒํผ์ด DOM์์ ๋ํ๋๊ธฐ ์ ์ ์คํ์ด ๋์ด๋ฒ๋ฆฌ๊ฑฐ๋ (๋ก๊ทธ์ธ ํ์ด์ง ๋ก๋ฉ์ด ๋๋ ค์), css ์ ๋๋ฉ์ด์ ์ด ๋๋์ผ ์ ์ง๋ ์ํ์์ ํด๋ฆญ ํ ์ ์๋ค๋์ง, ๋ฒํผ์ด ํน์ ์์ ์ diabled ๋์ด ์๋ค๋์ง, ๋ฒํผ์ด ์คํฌ๋กค์ ํด์ ํ์ด์ง์ ํ๋จ์ผ๋ก ๋ด๋ ค๊ฐ์ผ ํ๋ ์ํฉ๋ค์ด ์์ ๋ ํด๋ฆญ์ ์คํจํ๊ณค ํฉ๋๋ค. ๊ฐ๋ฐ์๋ ์๋ง๋ setTimeOut์ ์ด์ฉํ์ฌ 1์ด ์ ๋์ ์๊ฐ์ ์ฌ์ ๋ฅผ ๋ ๋ค์ ๋ฒํผ์ ํด๋ฆญํ๋ ๋ก์ง์ ๋ง๋ค์ด์ ํด๊ฒฐํ๋ค๊ณ ํ ์ ์์ง๋ง, ํญ์ ์๊ฐ์ ๋ณด์ฅํด์ฃผ๋ ๊ฒ์ ์๋๊ธฐ์ ์ฌ์ค ํด๊ฒฐํ๋ค๊ณ ๋งํ ์ ์์ต๋๋ค. setTimeOut์ ๋ํ flakiness๋ฅผ ๋ ์ ๋ฐํ๋๋ฐ ์์ค์ฝ๋๊ฐ ๋ฐ๋์ง ์์์์๋ ๋ถ๊ตฌํ๊ณ ๋ถ๊ท์นํ๊ฒ ํต๊ณผ ๋๋ ์คํจ ํ๋ ๊ฒ์ ๋งํฉ๋๋ค. ์ด๋ฐ ๋ถ๋ถ์ ๊ฐ์ ํ๊ธฐ ์ํด playwright์์ ๋ช๋ช ๋ฉ์๋์ ์๋์ผ๋ก ๊ธฐ๋ค๋ ค์ฃผ๋ ๊ธฐ๋ฅ์ด ์์ต๋๋ค.
// Playwright waits for #search element to be in the DOM
await page.fill('#search', 'query')
// Playwright waits for element to stop animating
// and accept clicks.
await page.click('#search')
๋ช ์์ ์ผ๋ก DOM์ ๋ํ๋ ๋๊น์ง ๋๋ ์์ด์ง ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๊ฒ ํ ์๋ ์์ต๋๋ค.
// Wait for #promo to become visible, for example with `visibility:visible`.
await page.waitForSelector('#promo')
// Wait for #search to appear in the DOM.
await page.waitForSelector('#search', { state: 'attached' })
// Wait for #details to become hidden, for example with `display:none`.
await page.waitForSelector('#details', { state: 'hidden' })
aoto-wait์ ์ง์ํ๋ method ๋ฆฌ์คํธ ๋ณด๊ธฐ
๋ค์ ํฌ์คํ ์์ playwirght ์ ์ด์ฉํ์ฌ ํ๊ฒฝ์ค์ ์ ๋ํด ๊ธ์ ์จ๋ณด๊ฒ ์ต๋๋ค.
์ฐธ๊ณ ์๋ฃ