Playwright + Django:如何等待事件

huangapple go评论78阅读模式
英文:

Playwright + Django: how to wait for events

问题

在我的测试中,我必须等待一个事件触发后才能继续进行测试断言,但我无法弄清楚如何让Playwright等待该事件。似乎Playwright无法看到这个事件。

一个简单的示例是使用django页面:点击按钮会触发一个名为 boop 的事件,该事件会更改文档的背景颜色。

模板 event.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Playwright Events</title>
</head>
<body><button>Click me</button>
<script>
const btn = document.querySelector("button");
btn.addEventListener("click", (e) => document.dispatchEvent(new Event("boop")));

document.addEventListener("boop", (e) => { document.body.style.backgroundColor = "darkcyan"; });
</script>
</body>
</html>

URL配置:

urlpatterns = [
    path('', TemplateView.as_view(template_name="event.html"))
]

测试代码:

def test(page, live_server):
    page.goto(live_server.url)
    page.wait_for_timeout(500)
    btn = page.get_by_text('Click me')
    with page.expect_event("boop", timeout=1000):
        btn.click()
    page.wait_for_timeout(500)

当在有头模式下运行测试时,你会看到背景颜色正在变化 - 这意味着事件 boop 已触发。但测试仍然失败,因为 expect_event 超时:
playwright._impl._api_types.TimeoutError: Timeout 1000ms exceeded while waiting for event "boop"

我一定做错了什么,但我无法弄清楚是什么。

找到了类似的 帖子,但它与django的playwright-python无关。

英文:

In my tests, I have to wait for an event to trigger before continuing with test assertions, but I can't figure out how to make Playwright wait for that event. It seems like Playwright can't see the event.

Simple example with a django page: clicking the button fires an event boop that changes the background color of the document.

Template event.html:

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;Playwright Events&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;&lt;button&gt;Click me&lt;/button&gt;
&lt;script&gt;
const btn = document.querySelector(&quot;button&quot;);
btn.addEventListener(&quot;click&quot;, (e) =&gt; document.dispatchEvent(new Event(&quot;boop&quot;)));

document.addEventListener(&quot;boop&quot;, (e) =&gt; { document.body.style.backgroundColor = &quot;darkcyan&quot;; });
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

URL conf:

urlpatterns = [
    path(&#39;&#39;, TemplateView.as_view(template_name=&quot;event.html&quot;))
]

The test:

def test(page, live_server):
    page.goto(live_server.url)
    page.wait_for_timeout(500)
    btn = page.get_by_text(&#39;Click me&#39;)
    with page.expect_event(&quot;boop&quot;, timeout=1000):
        btn.click()
    page.wait_for_timeout(500)

When running the test in headed mode, you can see the background color changing - meaning that the event boop was fired. But the test still fails because expect_event times out:
playwright._impl._api_types.TimeoutError: Timeout 1000ms exceeded while waiting for event &quot;boop&quot;

I must be doing something wrong, but I can't figure out what.

Found this similar post, but it is not about playwright-python with django.

答案1

得分: 3

这部分的翻译如下:

"It's not that explicit in the documentation but events refer to playwright events, not HTML events.

There is no support at the moment for the functionality you describe, you could emulate it as follows though:

class ExpectedHTMLEvent:
    def __init__(self, page: sync_api.Page, html_event: str, timeout_ms: float):
        self.page = page
        self.html_event = html_event
        self.timeout_ms = timeout_ms
        self.fn_name = f&quot;setFlag{&#39;&#39;.join(random.choices(string.ascii_lowercase, k=6))}&quot;
        self._flag = False
        self._end = None

    def _flag_setter(self):
        self._flag = True

    def __enter__(self) -&gt; None:
        self.page.expose_function(self.fn_name, self._flag_setter)
        opts = &quot;{once: true}&quot;
        self.page.evaluate(
            f&quot;document.addEventListener(&#39;{self.html_event}&#39;, async () =&gt; await window.{self.fn_name}(), {opts})&quot;
        )
        self._end = datetime.now() + timedelta(milliseconds=self.timeout_ms)

    def __exit__(self, __exc_type, __exc_value, __traceback) -&gt; None:
        while datetime.now() &lt; self._end:
            if self._flag:
                return
            time.sleep(0.1)

        raise TimeoutError(f&quot;&#39;{self.html_event}&#39; was never observed&quot;)


with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto(&#39;http://example.com/&#39;)

    with ExpectedHTMLEvent(page, &quot;boop&quot;, 2_000):
        page.evaluate(&quot;() =&gt; document.dispatchEvent(new Event(&#39;boop&#39;))&quot;)

    with ExpectedHTMLEvent(page, &quot;not boop&quot;, 2_000):
        page.evaluate(&quot;() =&gt; document.dispatchEvent(new Event(&#39;boop&#39;))&quot;)
TimeoutError: &#39;not boop&#39; was never observed
英文:

It's not that explicit in the documentation but events refer to playwright events, not HTML events.

There is no support at the moment for the functionality you describe, you could emulate it as follows though:

class ExpectedHTMLEvent:
    def __init__(self, page: sync_api.Page, html_event: str, timeout_ms: float):
        self.page = page
        self.html_event = html_event
        self.timeout_ms = timeout_ms
        self.fn_name = f&quot;setFlag{&#39;&#39;.join(random.choices(string.ascii_lowercase, k=6))}&quot;
        self._flag = False
        self._end = None

    def _flag_setter(self):
        self._flag = True

    def __enter__(self) -&gt; None:
        self.page.expose_function(self.fn_name, self._flag_setter)
        opts = &quot;{once: true}&quot;
        self.page.evaluate(
            f&quot;document.addEventListener(&#39;{self.html_event}&#39;, async () =&gt; await window.{self.fn_name}(), {opts})&quot;
        )
        self._end = datetime.now() + timedelta(milliseconds=self.timeout_ms)

    def __exit__(self, __exc_type, __exc_value, __traceback) -&gt; None:
        while datetime.now() &lt; self._end:
            if self._flag:
                return
            time.sleep(0.1)

        raise TimeoutError(f&quot;&#39;{self.html_event}&#39; was never observed&quot;)


with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto(&#39;http://example.com/&#39;)

    with ExpectedHTMLEvent(page, &quot;boop&quot;, 2_000):
        page.evaluate(&quot;() =&gt; document.dispatchEvent(new Event(&#39;boop&#39;))&quot;)

    with ExpectedHTMLEvent(page, &quot;not boop&quot;, 2_000):
        page.evaluate(&quot;() =&gt; document.dispatchEvent(new Event(&#39;boop&#39;))&quot;)
TimeoutError: &#39;not boop&#39; was never observed

huangapple
  • 本文由 发表于 2023年5月25日 22:37:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76333497.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定