Playwright 使用指南-2

Playwright 使用指南,Playwright 入门介绍 请参考另一篇博客

此博客为官方文档译文 希望读者可以快速了解 Playwriht 可以用来做什么,怎么用。有些专业名词可能翻译不准确哈

Playwrigh 使用指南-1

1 Auto-waiting 自动等待

Playwright 在执行操作之前对元素执行一系列可操作性检查,以确保这些操作按预期运行。它会自动等待所有相关检查通过,然后才执行请求的操作。如果所需的检查未在给定范围内通过timeout,则操作失败并显示TimeoutError.

例如,对于[page.click(selector, **kwargs)],Playwright 将确保:

  • 元素Attached 附加到 DOM
  • 元素Visible 可见
  • 元素是Stable 稳定的,就像没有动画或完成动画一样
  • 元素 Receives Events 接收事件,因为没有被其他元素遮挡
  • 元素已 Enabled 启用

以下是为每个操作执行的可操作性检查的完整列表:

ActionAttachedVisibleStableReceives EventsEnabledEditable
checkYesYesYesYesYes-
clickYesYesYesYesYes-
dblclickYesYesYesYesYes-
setCheckedYesYesYesYesYes-
tapYesYesYesYesYes-
uncheckYesYesYesYesYes-
hoverYesYesYesYes--
scrollIntoViewIfNeededYes-Yes---
screenshotYesYesYes---
fillYesYes--YesYes
selectTextYesYes----
dispatchEventYes-----
focusYes-----
getAttributeYes-----
innerTextYes-----
innerHTMLYes-----
pressYes-----
setInputFilesYes-----
selectOptionYesYes--Yes-
textContentYes-----
typeYes-----

Forcing actions 强制行为

page.click(selector, **kwargs)等一些操作支持force禁用非必要可操作性检查的选项,例如将真值传递forcepage.click(selector, **kwargs)方法不会检查目标元素是否实际接收到点击事件.

Attached

当元素连接到 Document 或 ShadowRoot时,它被认为是附加的。

Visible

当元素具有非空边界框并且没有visibility:hidden计算样式时,元素被认为是可见的。请注意,大小为零或 with 的元素display:none不被视为可见。

Stable

当元素在至少两个连续的动画帧中保持相同的边界框时,元素被认为是稳定的。

Enabled

元素被视为已启用,除非它是<button>、或具有属性。<select>``<input>``<textarea>``disabled

Editable

当元素被启用并且没有readonly设置属性时,它被认为是可编辑的。

Receives Events 接收事件

  • 当元素在动作点是指针事件的命中目标时,被认为接收指针事件。例如,当点击 点时(10;10),Playwright 会检查是否有其他元素(通常是叠加层)会捕获点击点(10;10)

    例如,考虑一个场景,Sign Up无论何时调用page.click(selector, **kwargs),Playwright 都会点击按钮:

    • 页面正在检查用户名是否唯一且Sign Up按钮已禁用;
    • 在与服务器核对后,禁用的Sign Up按钮将替换为另一个现在启用的按钮。

2 API testing API 测试

Playwright 可用于访问应用程序的 REST API

有时您可能希望直接从 Python 向服务器发送请求,而无需加载页面并在其中运行 js 代码。它可能会派上用场的几个例子:

  • 测试您的服务器 API。
  • 在测试中访问 Web 应用程序之前准备服务器端状态。
  • 在浏览器中运行一些操作后验证服务器端的后置条件。

所有这些都可以通过APIRequestContext方法来实现。

以下示例依赖于pytest-playwright将 Playwright 固定装置添加到 Pytest 测试运行器的包。

  • 编写 API 测试
  • 通过 API 调用准备服务器状态
  • 运行用户操作后检查服务器状态
  • 重用认证状态

2.1 Writing API Test 编写 API测试

APIRequestContext可以通过网络发送各种 HTTP(S) 请求。

以下示例演示了如何使用 Playwright 通过GitHub API测试问题创建。测试套件将执行以下操作:

  • 在运行测试之前创建一个新的存储库。
  • 创建一些问题并验证服务器状态。
  • 运行测试后删除存储库。

2.1.1 Configure 配置

GitHub API 需要授权,因此我们将为所有测试配置一次令牌。在此期间,我们还将设置baseURL以简化测试。

import os
from typing import Generator

import pytest
from playwright.sync_api import Playwright, APIRequestContext

GITHUB_API_TOKEN = os.getenv("GITHUB_API_TOKEN")
assert GITHUB_API_TOKEN, "GITHUB_API_TOKEN is not set"


@pytest.fixture(scope="session")
def api_request_context(
    playwright: Playwright,
) -> Generator[APIRequestContext, None, None]:
    headers = {
        # We set this header per GitHub guidelines.
        "Accept": "application/vnd.github.v3+json",
        # Add authorization token to all requests.
        # Assuming personal access token available in the environment.
        "Authorization": f"token {GITHUB_API_TOKEN}",
    }
    request_context = playwright.request.new_context(
        base_url="https://api.github.com", extra_http_headers=headers
    )
    yield request_context
    request_context.dispose()

2.1.2 Write tests 编写测试

现在我们初始化了请求对象,我们可以添加一些测试,这些测试将在存储库中创建新问题。

import os
from typing import Generator

import pytest
from playwright.sync_api import Playwright, APIRequestContext

GITHUB_API_TOKEN = os.getenv("GITHUB_API_TOKEN")
assert GITHUB_API_TOKEN, "GITHUB_API_TOKEN is not set"

GITHUB_USER = os.getenv("GITHUB_USER")
assert GITHUB_USER, "GITHUB_USER is not set"

GITHUB_REPO = "test"

# ...

def test_should_create_bug_report(api_request_context: APIRequestContext) -> None:
    data = {
        "title": "[Bug] report 1",
        "body": "Bug description",
    }
    new_issue = api_request_context.post(f"/repos/{GITHUB_USER}/{GITHUB_REPO}/issues", data=data)
    assert new_issue.ok

    issues = api_request_context.get(f"/repos/{GITHUB_USER}/{GITHUB_REPO}/issues")
    assert issues.ok
    issues_response = issues.json()
    issue = list(filter(lambda issue: issue["title"] == "[Bug] report 1", issues_response))[0]
    assert issue
    assert issue["body"] == "Bug description"

def test_should_create_feature_request(api_request_context: APIRequestContext) -> None:
    data = {
        "title": "[Feature] request 1",
        "body": "Feature description",
    }
    new_issue = api_request_context.post(f"/repos/{GITHUB_USER}/{GITHUB_REPO}/issues", data=data)
    assert new_issue.ok

    issues = api_request_context.get(f"/repos/{GITHUB_USER}/{GITHUB_REPO}/issues")
    assert issues.ok
    issues_response = issues.json()
    issue = list(filter(lambda issue: issue["title"] == "[Feature] request 1", issues_response))[0]
    assert issue
    assert issue["body"] == "Feature description"

2.1.3 Setup and teardown 安装和拆卸

这些测试假定存储库存在。您可能希望在运行测试之前创建一个新的,然后再将其删除。为此使用会话夹具。之前的部分yield是之前的部分,之后的部分是之后的部分。

# ...
@pytest.fixture(scope="session", autouse=True)
def create_test_repository(
    api_request_context: APIRequestContext,
) -> Generator[None, None, None]:
    # Before all
    new_repo = api_request_context.post("/user/repos", data={"name": GITHUB_REPO})
    assert new_repo.ok
    yield
    # After all
    deleted_repo = api_request_context.delete(f"/repos/{GITHUB_USER}/{GITHUB_REPO}")
    assert deleted_repo.ok

2.1.4 Complete test example 完整的测试示例

以下是 API 测试的完整示例:

from enum import auto
import os
from typing import Generator

import pytest
from playwright.sync_api import Playwright, Page, APIRequestContext, expect

GITHUB_API_TOKEN = os.getenv("GITHUB_API_TOKEN")
assert GITHUB_API_TOKEN, "GITHUB_API_TOKEN is not set"

GITHUB_USER = os.getenv("GITHUB_USER")
assert GITHUB_USER, "GITHUB_USER is not set"

GITHUB_REPO = "test"


@pytest.fixture(scope="session")
def api_request_context(
    playwright: Playwright,
) -> Generator[APIRequestContext, None, None]:
    headers = {
        # We set this header per GitHub guidelines.
        "Accept": "application/vnd.github.v3+json",
        # Add authorization token to all requests.
        # Assuming personal access token available in the environment.
        "Authorization": f"token {GITHUB_API_TOKEN}",
    }
    request_context = playwright.request.new_context(
        base_url="https://api.github.com", extra_http_headers=headers
    )
    yield request_context
    request_context.dispose()


@pytest.fixture(scope="session", autouse=True)
def create_test_repository(
    api_request_context: APIRequestContext,
) -> Generator[None, None, None]:
    # Before all
    new_repo = api_request_context.post("/user/repos", data={"name": GITHUB_REPO})
    assert new_repo.ok
    yield
    # After all
    deleted_repo = api_request_context.delete(f"/repos/{GITHUB_USER}/{GITHUB_REPO}")
    assert deleted_repo.ok


def test_should_create_bug_report(api_request_context: APIRequestContext) -> None:
    data = {
        "title": "[Bug] report 1",
        "body": "Bug description",
    }
    new_issue = api_request_context.post(
        f"/repos/{GITHUB_USER}/{GITHUB_REPO}/issues", data=data
    )
    assert new_issue.ok

    issues = api_request_context.get(f"/repos/{GITHUB_USER}/{GITHUB_REPO}/issues")
    assert issues.ok
    issues_response = issues.json()
    issue = list(
        filter(lambda issue: issue["title"] == "[Bug] report 1", issues_response)
    )[0]
    assert issue
    assert issue["body"] == "Bug description"


def test_should_create_feature_request(api_request_context: APIRequestContext) -> None:
    data = {
        "title": "[Feature] request 1",
        "body": "Feature description",
    }
    new_issue = api_request_context.post(
        f"/repos/{GITHUB_USER}/{GITHUB_REPO}/issues", data=data
    )
    assert new_issue.ok

    issues = api_request_context.get(f"/repos/{GITHUB_USER}/{GITHUB_REPO}/issues")
    assert issues.ok
    issues_response = issues.json()
    issue = list(
        filter(lambda issue: issue["title"] == "[Feature] request 1", issues_response)
    )[0]
    assert issue
    assert issue["body"] == "Feature description"

2.2 Prepare server state via API calls 通过API调用准备服务器状态

以下测试通过 API 创建一个新问题,然后导航到项目中所有问题的列表以检查它是否出现在列表顶部。使用LocatorAssertions执行检查。

def test_last_created_issue_should_be_first_in_the_list(api_request_context: APIRequestContext, page: Page) -> None:
    def create_issue(title: str) -> None:
        data = {
            "title": title,
            "body": "Feature description",
        }
        new_issue = api_request_context.post(
            f"/repos/{GITHUB_USER}/{GITHUB_REPO}/issues", data=data
        )
        assert new_issue.ok
    create_issue("[Feature] request 1")
    create_issue("[Feature] request 2")
    page.goto(f"https://github.com/{GITHUB_USER}/{GITHUB_REPO}/issues")
    first_issue = page.locator("a[data-hovercard-type='issue']").first
    expect(first_issue).to_have_text("[Feature] request 2")

2.3 Check the server state after running user actions 运行用户操作后检查服务器状态

以下测试通过浏览器中的用户界面创建一个新问题,然后通过 API 检查它是否已创建:

def test_last_created_issue_should_be_on_the_server(api_request_context: APIRequestContext, page: Page) -> None:
    page.goto(f"https://github.com/{GITHUB_USER}/{GITHUB_REPO}/issues")
    page.locator("text=New issue").click()
    page.locator("[aria-label='Title']").fill("Bug report 1")
    page.locator("[aria-label='Comment body']").fill("Bug description")
    page.locator("text=Submit new issue").click()
    issue_id = page.url.split("/")[-1]

    new_issue = api_request_context.get(f"https://github.com/{GITHUB_USER}/{GITHUB_REPO}/issues/{issue_id}")
    assert new_issue.ok
    assert new_issue.json()["title"] == "[Bug] report 1"
    assert new_issue.json()["body"] == "Bug description"

2.4 Reuse authentication state 重用的认证状态

Web 应用程序使用基于 cookie 或基于令牌的身份验证,其中经过身份验证的状态存储为cookie。Playwright 提供了api_request_context.storage_state(**kwargs)方法,该方法可用于从经过身份验证的上下文中检索存储状态,然后使用该状态创建新的上下文。

存储状态在BrowserContextAPIRequestContext之间是可互换的。您可以使用它通过 API 调用登录,然后使用已有的 cookie 创建新的上下文。以下代码片段从经过身份验证的APIRequestContext 中检索状态,并使用该状态创建一个新的BrowserContext

request_context = playwright.request.new_context(http_credentials={"username": "test", "password": "test"})
request_context.get("https://api.example.com/login")
# Save storage state into a variable.
state = request_context.storage_state()

# Create a new context with the saved storage state.
context = browser.new_context(storage_state=state)

3. Assertions 断言

Playwright 为您提供了 Web-First Assertions 以及方便的方法来创建断言,这些断言将等待并重试,直到满足预期的条件。

考虑以下示例:

同步

from playwright.sync_api import Page, expect

def test_status_becomes_submitted(page: Page) -> None:
    # ..
    page.locator("#submit-button").click()
    expect(page.locator(".status")).to_have_text("Submitted")

异步

from playwright.async_api import Page, expect

async def test_status_becomes_submitted(page: Page) -> None:
    # ..
    await page.locator("#submit-button").click()
    await expect(page.locator(".status")).to_have_text("Submitted")

Playwright 将使用选择器重新测试节点,.status直到获取的节点具有"Submitted"文本。它将重新获取节点并一遍又一遍地检查它,直到满足条件或达到超时。您可以将此超时作为选项传递。

默认情况下,断言超时设置为 5 秒。

详细使用请参考官方文档

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
Python Playwright是一个能够控制浏览器进行自动化测试和操作的工具。其中,-remote-debugging-port是Playwright库提供的一个选项,用于指定远程调试的端口号。 使用-remote-debugging-port选项,可以让Playwright通过特定的端口与浏览器建立调试和通信的连接。这样一来,我们就可以实时地监控浏览器执行的情况,以便进行调试或将其集成到其他自动化工具中。 举个例子,假设我们想要在Playwright中使用Chrome进行自动化操作,并在开始之前设置远程调试端口号为9222。我们可以这样编写代码: ```python from playwright.sync_api import sync_playwright def main(): with sync_playwright() as playwright: browser_type = playwright.chromium browser = browser_type.launch(remote_debugging_port=9222) page = browser.new_page() # 在这里可以进行各种自动化操作 # ... browser.close() if __name__ == '__main__': main() ``` 在上述代码中,我们使用了playwright.sync_api中的sync_playwright函数来创建一个Playwright对象。然后,我们选择了要使用的浏览器,这里是Chrome。接着,我们通过调用browser_type的launch方法来启动浏览器,并使用remote_debugging_port参数指定了远程调试的端口号9222。 这样,我们就成功地在Playwright中设置了远程调试端口号。接下来,我们可以根据需要在此基础上进行各种自动化测试和操作,并通过与浏览器建立的调试连接实时监控其执行情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

度假的小鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值