yarn init playwright # root 디렉토리
yarn init playwright new-project # 또는 새로운 프로젝트 만들기
yarn add -D @playwright/test
yarn playwright install
# 또는 특정 브라우저만 설치
yarn playwright install webkit
# 지원하는 모든 브라우저 확인
yarn playwright install --help
테스트 작성
(예시) tests
폴더 > example.spec.ts
=> 테스트를 정의하는 파일
import { test, expect } from '@playwright/test';
test('basic test', async ({ page }) => {
await page.goto('localhost:3000');
const title = page.locator('title');
await expect(title).toHaveText('루닛케어');
});
(예시) playwright.config.js => 옵션을 설정 (글로벌 또는 로컬)
=> 작성한 configuration 설정을 통해 다수의 브라우저에 대해 테스트를 실행할 수 있음
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
use: {
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
};
export default config;
yarn playwright test # headless (Chronium browser)
yarn playwright test --headed # headed
테스트를 만들 수 있는 방법
yarn playwright codegen localhost:3000
playwright inspector 창을 통해서 테스트 코드를 함께 확인해볼 수 있다
test.only('focus this test', async ({ page }) => {
// Run only focused tests in the entire project.
});
test.skip('skip this test', async ({ page }) => {
// This test is not run
});
+ 특정 조건의 테스트 스킵
test('skip this test', async ({ page, browserName }) => {
test.skip(browserName === 'firefox', 'Still working on it');
});
test.describe('two tests', () => {
test('one', async ({ page }) => {
// ...
});
test('two', async ({ page }) => {
// ...
});
});
test('Test login page @fast', async ({ page }) => {
// ...
});
yarn playwright test --grep @fast # 해당 태그의 테스트만 수행
yarn playwright test --grep invert @fast # 해당 태그의 텍스트 skip
playwright은 expect 라이브러리 사용 => 테스트를 작성하면서 특정 조건을 체크해야하는 경우
( toEqual
, toContain
, toMatch
..)
expect(success).toBeTruthy()
또한 async matchers 제공함으로써 기대하는 조건이 충족될때까지 기다릴 수 있다
(반대의 경우 .not
키워드)
expect(value).not.toEqual(0);
await expect(locator).not.toContainText("some text");
matching
(예시)
await expect(page.locator('.status')).toHaveText('Submitted');
node가 'Submitted'
텍스트를 가질 때까지 playwright test는 .status
selector를 가진 노드를 re-testing
(해당 조건이 만족하거나 timeout이 될 때까지 반복적으로 re-fetching 하고 checking)
(방법1)
beforeEach
를 활용하여 각 test 이전에 로그인 로직을 실행 (매번 로그인을 실행하는 것은 테스트 수행 효율을 낮추게 됨 => 로그인 상태를 재사용하는 아래의 방법)
test.beforeEach(async({page})=> {
await page.goto('localhost:3000/signin');
await page.type('[name=email]', 'test@lunit.io');
await page.type('[name=password]', 'password~~~');
await page.keyboard.press('Enter',{delay: 3000});
})
test('first', async({page})=> {
})
test('second', async({page})=> {
})
(방법2)
signed-in state를 재사용 (로그인을 한번만 수행)
=> global-setup
스크립트 작성
// global-setup.ts
import { chromium, FullConfig } from '@playwright/test';
async function globalSetup(config: FullConfig) {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('localhost:3000/signin');
await page.type('[name=email]', 'test@lunit.io');
await page.type('[name=password]', 'password~~~');
await page.keyboard.press('Enter');
// Save signed-in state to 'storageState.json'.
await page.context().storageState({ path: 'storageState.json' });
await browser.close();
}
export default globalSetup;
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
globalSetup: require.resolve('./tests/global-setup'),
use: {
storageState: 'storageState.json'
},
projects: [
// ..
],
};
export default config;
default browser
, context
, page
설정을 할 수 있다
Global Configuration : 전역적으로 적용되도록 설정
Local Configuration : 특정 테스트 파일에서 별도로 설정하여 override
test.use
또는 test.describe
블록 사용
Basic Options
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
use: {
headless: true,
baseURL: 'http://localhost:3000'
},
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
],
};
export default config;
다수의 독립적인 브라우저 세션을 운영할 수 있도록 제공
browser.newContext()
: 새로운 브라우저 컨텍스트를 생성 (다른 browser context와 쿠키, 캐시를 공유 x)browser.newPage()
: 새로운 브라우저 컨텍스트에서 새로운 페이지 생성 (해당 page를 닫으면 context 또한 종료)test
테스트를 정의하기
test.describe
테스트들의 그룹을 설정할 수 있다
test.describe('test', ()=> {
})
test.step
테스트의 세부적인 step을 설정할 수 있다
// 예시
test.describe('first test', async()=> {
test('test', async({page})=> {
await test.step('Navigate to SignIn Page', async()=> {
await page.goto('localhost:3000/signin');
})
await test.step('Sign In', async()=> {
await page.type('[name=email]', 'test@lunit.io');
await page.type('[name=password]', 'password~~~');
await page.keyboard.press('Enter',{delay: 3000});
})
})
})
test.fixme
테스트가 fix 되어야한다는 의도에서 사용 => 해당 코드 만나면 테스트가 중단됨(skip)
test.fixme(condition, description)
condition === true
→ 테스트 중단test('broken in WebKit', async ({ page, browserName }) => {
test.fixme(browserName === 'webkit', 'This feature is not implemented on Mac yet');
// ...
});
const hrefElement = await page.$('a');
await hrefElement.click();
=> ElementHandle
을 통해 DOM element를 조작할 수 있는데, 공식문서에 따르면 이는 지양하고 Locator
사용을 권장하고 있다
페이지 내에서 element를 찾을 수 있도록 하며, 특정 action을 수행할 수 있음
page.locator() # locator 생성
locator.check([options])
locator.click([options])
locator.fill(value[,options])
locator.innerText([options])
locator.setChecked(checked[, options])
Locator를 생성하는 데에 사용되는 string
await page.locator('text=Log in').click(); # text selector
await page.locator('button').click(); # css selector
# <article> 태그 내에서 탐색
await page.locator('article:has-text("Playwright")').click() # text + css selector
(예시)
await page.locator('id=username', 'value').fill(); # input값 채우기
await page.locator('data-test-id=submit').click();
하나의 Browser 인스턴스는 다수의 Page 인스턴스를 가질 수 있음
Page는 browser context에서 하나의 tab 혹은 popup 이라고 할 수 있다
다른 URL로 이동할 때 사용될 수 있다
const page = await context.newPage();
await page.goto('http://example.com');
[작성 시 고려할 점]
test 파일은 페이지 단위로 작성한다
consumer.spec.ts
❌ => 어떤 페이지에 해당하는 테스트인지 파악하기 어려움signin.spect.ts
=> 로그인 페이지에 해당하는 테스트test.describe
로 테스트에 대한 description을 정의하고 안에 test문을 통해 특정 테스트를 작성한다
describe('signin', async () => {
test('example1', async ({ page }) => {
// .. example1
})
test('example2', async({ page }) => {
// .. example2
})
})