英文:
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
:
<!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 conf:
urlpatterns = [
path('', TemplateView.as_view(template_name="event.html"))
]
The test:
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)
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 "boop"
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"setFlag{''.join(random.choices(string.ascii_lowercase, k=6))}"
self._flag = False
self._end = None
def _flag_setter(self):
self._flag = True
def __enter__(self) -> None:
self.page.expose_function(self.fn_name, self._flag_setter)
opts = "{once: true}"
self.page.evaluate(
f"document.addEventListener('{self.html_event}', async () => await window.{self.fn_name}(), {opts})"
)
self._end = datetime.now() + timedelta(milliseconds=self.timeout_ms)
def __exit__(self, __exc_type, __exc_value, __traceback) -> None:
while datetime.now() < self._end:
if self._flag:
return
time.sleep(0.1)
raise TimeoutError(f"'{self.html_event}' was never observed")
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('http://example.com/')
with ExpectedHTMLEvent(page, "boop", 2_000):
page.evaluate("() => document.dispatchEvent(new Event('boop'))")
with ExpectedHTMLEvent(page, "not boop", 2_000):
page.evaluate("() => document.dispatchEvent(new Event('boop'))")
TimeoutError: 'not boop' 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"setFlag{''.join(random.choices(string.ascii_lowercase, k=6))}"
self._flag = False
self._end = None
def _flag_setter(self):
self._flag = True
def __enter__(self) -> None:
self.page.expose_function(self.fn_name, self._flag_setter)
opts = "{once: true}"
self.page.evaluate(
f"document.addEventListener('{self.html_event}', async () => await window.{self.fn_name}(), {opts})"
)
self._end = datetime.now() + timedelta(milliseconds=self.timeout_ms)
def __exit__(self, __exc_type, __exc_value, __traceback) -> None:
while datetime.now() < self._end:
if self._flag:
return
time.sleep(0.1)
raise TimeoutError(f"'{self.html_event}' was never observed")
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('http://example.com/')
with ExpectedHTMLEvent(page, "boop", 2_000):
page.evaluate("() => document.dispatchEvent(new Event('boop'))")
with ExpectedHTMLEvent(page, "not boop", 2_000):
page.evaluate("() => document.dispatchEvent(new Event('boop'))")
TimeoutError: 'not boop' was never observed
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论