<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`