playwright.goto doesn't handle redirect well

This commit is contained in:
Lendemor 2024-11-27 20:34:34 +01:00
parent 24ff29f74d
commit c03d32c75f
2 changed files with 303 additions and 0 deletions

View File

@ -0,0 +1,237 @@
"""Integration test for dynamic routes."""
from __future__ import annotations
from typing import Generator, Type
import pytest
from playwright.sync_api import Page, expect
from reflex.testing import AppHarness, AppHarnessProd
def DynamicRoute():
"""App for testing dynamic routes."""
from typing import List
import reflex as rx
class DynamicState(rx.State):
order: List[str] = []
def on_load(self):
page_data = f"{self.router.page.path}-{self.page_id or 'no page id'}"
print(f"on_load: {page_data}")
self.order.append(page_data)
def on_load_redir(self):
query_params = self.router.page.params
page_data = f"on_load_redir-{query_params}"
print(f"on_load_redir: {page_data}")
self.order.append(page_data)
return rx.redirect(f"/page/{query_params['page_id']}")
@rx.var
def next_page(self) -> str:
try:
return str(int(self.page_id) + 1)
except ValueError:
return "0"
def index():
return rx.fragment(
# rx.input(
# value=DynamicState.router.session.client_token,
# read_only=True,
# id="token",
# ),
rx.input(value=rx.State.page_id, read_only=True, id="page_id"), # type: ignore
rx.input(
value=DynamicState.router.page.raw_path, read_only=True, id="raw_path"
),
rx.vstack(
rx.link("index", href="/", id="link_index"),
rx.link("page_X", href="/static/x", id="link_page_x"),
rx.link(
"next",
href="/page/" + DynamicState.next_page,
id="link_page_next", # type: ignore
),
rx.link("missing", href="/missing", id="link_missing"),
),
rx.list( # type: ignore
rx.foreach(
DynamicState.order, # type: ignore
lambda i: rx.list_item(rx.text(i)),
),
id="order",
),
rx.list( # type: ignore
rx.foreach(
DynamicState.router.page.params,
lambda i: rx.list_item(
rx.text(f"{i[0]}: {i[1]}"), # type: ignore
),
),
id="params",
),
)
class ArgState(rx.State):
"""The app state."""
@rx.var
def arg(self) -> int:
return int(self.arg_str or 0)
class ArgSubState(ArgState):
@rx.var(cache=True)
def cached_arg(self) -> int:
return self.arg
@rx.var(cache=True)
def cached_arg_str(self) -> str:
return self.arg_str
@rx.page(route="/arg/[arg_str]")
def arg() -> rx.Component:
return rx.vstack(
rx.data_list.root(
rx.data_list.item(
rx.data_list.label("rx.State.arg_str (dynamic)"),
rx.data_list.value(rx.State.arg_str, id="state-arg_str"), # type: ignore
),
rx.data_list.item(
rx.data_list.label("ArgState.arg_str (dynamic) (inherited)"),
rx.data_list.value(ArgState.arg_str, id="argstate-arg_str"), # type: ignore
),
rx.data_list.item(
rx.data_list.label("ArgState.arg"),
rx.data_list.value(ArgState.arg, id="argstate-arg"),
),
rx.data_list.item(
rx.data_list.label("ArgSubState.arg_str (dynamic) (inherited)"),
rx.data_list.value(ArgSubState.arg_str, id="argsubstate-arg_str"), # type: ignore
),
rx.data_list.item(
rx.data_list.label("ArgSubState.arg (inherited)"),
rx.data_list.value(ArgSubState.arg, id="argsubstate-arg"),
),
rx.data_list.item(
rx.data_list.label("ArgSubState.cached_arg"),
rx.data_list.value(
ArgSubState.cached_arg, id="argsubstate-cached_arg"
),
),
rx.data_list.item(
rx.data_list.label("ArgSubState.cached_arg_str"),
rx.data_list.value(
ArgSubState.cached_arg_str, id="argsubstate-cached_arg_str"
),
),
),
rx.link("+", href=f"/arg/{ArgState.arg + 1}", id="next-page"),
align="center",
height="100vh",
)
@rx.page(route="/redirect-page/[page_id]", on_load=DynamicState.on_load_redir) # type: ignore
def redirect_page():
return rx.fragment(rx.text("redirecting..."))
app = rx.App(state=rx.State)
app.add_page(index, route="/page/[page_id]", on_load=DynamicState.on_load) # type: ignore
app.add_page(index, route="/static/x", on_load=DynamicState.on_load) # type: ignore
app.add_page(index)
app.add_custom_404_page(index, on_load=DynamicState.on_load) # type: ignore
@pytest.fixture(scope="module")
def dynamic_route(
app_harness_env: Type[AppHarness], tmp_path_factory
) -> Generator[AppHarness, None, None]:
"""Start DynamicRoute app at tmp_path via AppHarness.
Args:
app_harness_env: either AppHarness (dev) or AppHarnessProd (prod)
tmp_path_factory: pytest tmp_path_factory fixture
Yields:
running AppHarness instance
"""
with app_harness_env.create(
root=tmp_path_factory.mktemp("dynamic_route"),
app_name=f"dynamicroute_{app_harness_env.__name__.lower()}",
app_source=DynamicRoute,
) as harness:
yield harness
def test_on_load_navigate(dynamic_route: AppHarness, page: Page):
assert dynamic_route.frontend_url is not None
is_prod = isinstance(dynamic_route, AppHarnessProd)
page.goto(dynamic_route.frontend_url)
# click the next link 10 times
exp_order = [f"/page/[page_id]-{ix}" for ix in range(10)]
link = page.locator("#link_page_next")
for ix in range(10):
link.click()
expect(page.locator("#page_id")).to_have_value(str(ix))
expect(page).to_have_url(dynamic_route.frontend_url + f"/page/{ix}/")
order = page.locator("#order")
expect(order).to_have_text("".join(exp_order))
# manually load the next page to trigger client side routing in prod mode
if is_prod:
exp_order += ["/404-no page id"]
exp_order += ["/page/[page_id]-10"]
page.goto(dynamic_route.frontend_url + "/page/10/")
expect(order).to_have_text("".join(exp_order))
# make sure internal nav still hydrates after redirect
exp_order += ["/page/[page_id]-11"]
link.click()
expect(order).to_have_text("".join(exp_order))
# load same page with a query param and make sure it passes through
if is_prod:
exp_order += ["/404-no page id"]
exp_order += ["/page/[page_id]-11"]
page.goto(f"{page.url}?foo=bar")
expect(order).to_have_text("".join(exp_order))
params = page.locator("#params")
params_str = params.text_content()
assert params_str and "foo: bar" in params_str
# test 404 page and make sure we hydrate
exp_order += ["/404-no page id"]
page.goto(dynamic_route.frontend_url + "/missing")
expect(page).to_have_url(dynamic_route.frontend_url + "/missing/")
# At that point we're on the 404 page, so #order is not rendered
expect(order).to_have_text("".join(exp_order))
# browser nav should still trigger hydration
if is_prod:
exp_order += ["/404-no page id"]
exp_order += ["/page/[page_id]-11"]
page.go_back()
expect(order).to_have_text("".join(exp_order))
# next/link to a 404 and ensure we still hydrate
exp_order += ["/404-no page id"]
missing_link = page.locator("#link_missing")
missing_link.click()
expect(order).to_have_text("".join(exp_order))
# hit a page that redirects back to dynamic page
if is_prod:
exp_order += ["/404-no page id"]
exp_order += ["on_load_redir-{'foo': 'bar', 'page_id': '0'}", "/page/[page_id]-0"]
page.goto(dynamic_route.frontend_url + "/redirect-page/0/")
# # should have redirected to page 0
# expect(page).to_have_url(dynamic_route.frontend_url + "/page/0/")

View File

@ -0,0 +1,66 @@
from typing import Generator
import pytest
from playwright.sync_api import Page, expect
from reflex.testing import AppHarness
def RedirectRoute():
"""App for testing redirects."""
import reflex as rx
class RedirectState(rx.State):
redirected: bool = False
order: list[str] = []
def on_load(self):
self.redirected = True
page_data = f"{self.router.page.path}-{self.page_id or 'no page id'}"
print(f"on_load: {page_data}")
self.order.append(page_data)
def on_load_redir(self):
page_id = self.router.page.params["page_id"]
return rx.redirect(f"/redirected/{page_id}")
app = rx.App(state=rx.State)
def index():
return rx.fragment(
rx.link("redirect", href="/redirect-page/3", id="link_redirect"),
rx.ordered_list(
rx.foreach(RedirectState.order, rx.text),
),
)
# reuse the same page, only the URL interests us
app.add_page(index, "index")
app.add_page(index, "redirect-page/[page_id]", on_load=RedirectState.on_load_redir)
app.add_page(index, "redirected/[page_id]", on_load=RedirectState.on_load)
@pytest.fixture(scope="module")
def redirect_repro(tmp_path_factory) -> Generator[AppHarness, None, None]:
with AppHarness.create(
app_source=RedirectRoute,
root=tmp_path_factory.mktemp("redirect_repro"),
) as harness:
assert harness.app_instance is not None, "app is not running"
yield harness
def test_redirect(redirect_repro: AppHarness, page: Page):
assert redirect_repro.frontend_url is not None
page.goto(redirect_repro.frontend_url)
# by clicking the link, we should be redirected to /redirected
page.click("#link_redirect")
expect(page).to_have_url(redirect_repro.frontend_url + "/redirected/3/")
# return to index
page.goto(redirect_repro.frontend_url)
expect(page).to_have_url(redirect_repro.frontend_url + "/")
page.goto(redirect_repro.frontend_url + "/redirect/3")
expect(page).to_have_url(redirect_repro.frontend_url + "/redirected/3/")