---
# System prepended metadata

title: Playwright 範例
tags: [Playwright, End to End]

---

<style>
.center{
    display: flex;
    justify-content: center;
}
.large_txt{
    font-size: x-large;
}

.lighten_txt{
    color: #aa0000;
}
</style>

# Playwright 範例

<a class="center large_txt" href="http://www.uitestingplayground.com/">
UI Test Automation Playground
</a>


### <a href='http://www.uitestingplayground.com/classattr'>Class Attribute</a>
撰寫一個每次執行時都觸發 alert popup 的測試案例.
- 獲取 <span class="lighten_txt">dialog</span> 訊息 
- 透過 <span class="lighten_txt">css class</span> 及屬性取得指定元件

```python
from playwright.sync_api import sync_playwright


def handle_dialog(dialog):
    print(dialog.type)
    print(dialog.message)
    dialog.dismiss()


def run(playwright):
    browser = playwright.chromium.launch(headless=False, slow_mo=1000)
    context = browser.new_context()

    page = context.new_page()

    page.goto("http://www.uitestingplayground.com/classattr")
    
    # page.on("dialog", lambda dialog: print(dialog.message))
    # 只有 lambda dialog: print(dialog.message) 會卡在彈跳視窗, 
    # 需要在透過 dialog.dismiss() 關閉
    # playwright 在處理 dialog 事件會自動呼叫 dialog.dismiss()
    
    page.once("dialog", handle_dialog)
    
    page.click(".btn-primary:has-text('Button')")

    context.close()
    browser.close()


with sync_playwright() as playwright:
    run(playwright)

```





### <a href="http://www.uitestingplayground.com/loaddelay">Load Delays</a>
撰寫一個經過等待頁面轉換後, 偵測指定元件的測試案例.
- Auto-waiting
- 透過 <span class="lighten_txt">css class</span> 及屬性取得指定元件

```python
def test_load_delay(page):
    page.goto("http://www.uitestingplayground.com/loaddelay")
    
    assert page.query_selector(".btn-primary:has-text('Button Appearing After Delay')").is_visible()
```



### <a href="http://www.uitestingplayground.com/ajax">AJAX Data</a>
撰寫一個可以等待指定元件出現的測試案例.
- Auto-waiting
- 透過 <span class="lighten_txt">ID</span> 和 <span class="lighten_txt">DOM結構</span> 取得指定元件

```python
def test_ajax(page):
    page.goto("http://www.uitestingplayground.com/ajax")
    page.click("text=Button Triggering AJAX Request")
    
    assert page.inner_text('#content > p:nth-child(1):has-text("Data loaded with AJAX get request.")')
    # assert page.inner_text("div[id='content'] p:nth-child(1):has-text('Data loaded with AJAX get request.')")
```
- [補充 nth 用法](https://playwright.dev/python/docs/selectors/#pick-n-th-match-from-the-query-result)




### <a href="http://www.uitestingplayground.com/textinput">Text Input</a>
撰寫一個輸入訊息後, 可以正確修改元件內容的測試案例.
- 透過 <span class="lighten_txt">ID</span> 取得指定元件
- <span class="lighten_txt">fill()</span>
:::spoiler
```python
def test_text_input(page):
    page.goto("http://www.uitestingplayground.com/textinput")
    
    assert page.query_selector(
        "#updatingButton").inner_text() == "Button That Should Change it's Name Based on Input Value"
        
    new_button_name = 'Change_btn_Name'
    page.fill(selector='#newButtonName', value=new_button_name)
    page.click(selector='#updatingButton')
    
    assert page.query_selector("#updatingButton").inner_text() == new_button_name
```
:::


### <a href="http://www.uitestingplayground.com/dynamictable">Dynamic Table</a>
撰寫一個可以在動態表格取得指定值的測試.
- 透過 <span class="lighten_txt">DOM</span> 和透過 <span class="lighten_txt">nth</span> 取得指定元件

:::spoiler
```python
def get_chrome_row(target_list: list) -> str:
    for row in target_list:
        if 'Chrome' in row:
            return row
    return 'Not Found'


def test_dynamic_table(page):
    page.goto("http://www.uitestingplayground.com/dynamictable")
    page.query_selector(selector="div[role=table]").inner_text()

    # 取得表的內容
    table_content_by_path = page.query_selector(
        "body>section:nth-child(2)>div:nth-child(1)>div:nth-child(6)>div:nth-child(3)").inner_text()
    table_content_by_role = page.query_selector(selector="div[role=table]").inner_text()

    assert table_content_by_path in table_content_by_role

    # 將 tab 以 空格 替換, 並將表的內容以換行切割
    table_list = table_content_by_role.replace('\t', ' ').split('\n')
    print(table_list)

    chrome_row = get_chrome_row(target_list=table_list)

    warning_text = page.query_selector('.bg-warning').inner_text()
    print(warning_text)

    # print(warning_text.split(': ')[1]) →取得 CPU ?%
    # print(table_list[0])
    assert warning_text.split(': ')[1] in chrome_row
```
:::



### <a href="http://www.uitestingplayground.com/progressbar">Progress Bar</a>
撰寫一個可以在指定條件按下暫停的測試.
- 透過 <span class="lighten_txt">ID</span> 及屬性得指定元件

```python
def test_progressbar(page):
    page.goto("http://www.uitestingplayground.com/progressbar")

    page.click('#startButton')
    
    # page.inner_text("#progressBar[aria-valuenow='75']")
    page.inner_text("#progressBar:has-text('75%')")
    print(page.query_selector("#progressBar").inner_html())
    
    page.click('#stopButton')
    
    assert "Result: 0" in page.inner_text('#result')
```




### <a href="http://www.uitestingplayground.com/visibility">Visibility</a>
撰寫一個可以檢查元件是否有被正確隱藏的測試.
- 透過 <span class="lighten_txt">ID</span> 及屬性得指定元件
- <span class="lighten_txt">bouding_box()</span>

:::spoiler
```python
def assert_find_attribute_status(page, target: str, attribute: str, status: str):
    assert status in page.query_selector(f"#{target}").get_attribute(attribute)


def assert_compare_position(page):
    hiding_layer = page.query_selector("#hidingLayer").bounding_box()
    overlapped_button = page.query_selector("#overlappedButton").bounding_box()
    assert int(hiding_layer['x']) == int(overlapped_button['x']) and \
           int(hiding_layer['y']) == int(overlapped_button['y'])


def test_visibility(page):
    page.goto('http://www.uitestingplayground.com/visibility')

    # before click, all button is visible
    for item in page.query_selector_all(".btn"):
        assert item.is_visible()

    page.click(selector="#hideButton")

    # after click hideButton, check btn status
    assert page.query_selector("#removedButton") is None

    assert_find_attribute_status(page=page, 
                                 target='zeroWidthButton', 
                                 attribute='class', 
                                 status='zerowidth')
    # assert page.query_selector('#zeroWidthButton').is_hidden()

    assert_compare_position(page)

    assert_find_attribute_status(page=page, 
                                 target='transparentButton', 
                                 attribute='style', 
                                 status='opacity: 0;')

    # assert page.query_selector('#invisibleButton').is_hidden()

    assert_find_attribute_status(page=page, 
                                 target='notdisplayedButton', 
                                 attribute='style', 
                                 status='display: none;')
    # assert page.query_selector('#notdisplayedButton').is_hidden()

    assert_find_attribute_status(page=page, 
                                 target='offscreenButton', 
                                 attribute='class', 
                                 status='offscreen')
```
:::

### <a href="http://www.uitestingplayground.com/sampleapp">Sample App</a>
撰寫一個可以檢視登入狀態的測試.
- <span class="lighten_txt">css with[ ]</span>
- <span class="lighten_txt">.css class with[ ]</span>
- <span class="lighten_txt">css class</span>

:::spoiler
```python
def test_sampleapp(page):
    page.goto('http://www.uitestingplayground.com/sampleapp')
    # before login
    assert page.query_selector("#loginstatus").inner_text() == 'User logged out.'
    assert page.query_selector(".btn-primary").inner_text() == 'Log In'

    # login
    user_login_name = 'ooo'
    page.query_selector(".form-control[name='UserName']").fill(value=user_login_name)
    page.query_selector(".form-control[name='Password']").fill(value='pwd')
    page.query_selector(".btn-primary").click()

    # after login
    assert page.query_selector("#loginstatus").inner_text() == f'Welcome, {user_login_name}!'
    assert page.query_selector(".btn-primary").inner_text() == 'Log Out'
```

```python
def test_sampleapp_with_wrong_password(page):
    page.goto('http://www.uitestingplayground.com/sampleapp')

    # login
    user_login_name = 'ooo'
    page.query_selector("css=[name=UserName]").fill(value=user_login_name)
    page.query_selector(".form-control[name='Password']").fill(value='777')
    page.query_selector(".btn-primary").click()

    assert page.query_selector("#loginstatus").inner_text() == "Invalid username/password"
```
:::


### <a href="http://www.uitestingplayground.com/mouseover">Mouse Over</a>
撰寫一個即使 css style 變動時, 還能正確執行的測試.
- <span class="lighten_txt">get_attribute()</span>
- <span class="lighten_txt">hover()</span>
- <span class="lighten_txt">dblclick()</span>

:::spoiler
```python
def test_mouseover(page):
    page.goto('http://www.uitestingplayground.com/mouseover')

    # before hover
    assert page.query_selector(selector=".text-primary:text('Click me')").get_attribute('title') == 'Click me'

    # hover
    page.query_selector(selector=":text('Click me')").hover()
    assert page.query_selector(selector=":text('Click me')").get_attribute('title') == 'Active Link'

    # dbclick
    page.query_selector(selector=":text('Click me')").dblclick()
    assert int(page.query_selector(selector="#clickCount").inner_text()) == 2
```
:::


### <a href="http://www.uitestingplayground.com/scrollbars">Scrollbars</a>
寫一個透過scroll操作, 找出隱藏在 Scrollview 中按鈕的測試.
- <span class="lighten_txt">bounding_box()</span>
- <span class="lighten_txt">透過多個Css Class找目標</span>
- <span class="lighten_txt">scroll_into_view_if_needed()</span>
- <span class="lighten_txt">wait_for_timeout</span>

:::spoiler
```python
def test_scroll_view(page):
    page.goto("http://www.uitestingplayground.com/scrollbars")

    find_hiding_button = page.query_selector(selector=".btn.btn-primary")
    before_scroll = page.query_selector(selector=".btn.btn-primary").bounding_box()
    find_hiding_button.scroll_into_view_if_needed()
    after_scroll = page.query_selector(selector=".btn.btn-primary").bounding_box()

    page.wait_for_timeout(timeout=1000)
    print(f'Before:{before_scroll}, After{after_scroll}')
```
:::

<hr>

<!-- [End to End 目錄](https://hackmd.io/F41miM4aRP2eljbPy6grCg) -->

###### tags: `Playwright` `End to End`