mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-27 18:56:31 +08:00
### What problem does this PR solve? Refactor user REST API ### Type of change - [x] Refactoring
257 lines
7.5 KiB
Python
257 lines
7.5 KiB
Python
import json
|
|
import os
|
|
|
|
import pytest
|
|
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
|
|
from playwright.sync_api import expect
|
|
|
|
from test.playwright.helpers.auth_selectors import (
|
|
AUTH_STATUS,
|
|
EMAIL_INPUT,
|
|
NICKNAME_INPUT,
|
|
PASSWORD_INPUT,
|
|
REGISTER_TAB,
|
|
SUBMIT_BUTTON,
|
|
)
|
|
from test.playwright.helpers.flow_steps import flow_params, require
|
|
from test.playwright.helpers.response_capture import capture_response_json
|
|
|
|
RESULT_TIMEOUT_MS = 15000
|
|
|
|
|
|
def _debug_register_response(page, response_info: dict) -> None:
|
|
if not os.getenv("PW_DEBUG_DUMP"):
|
|
return
|
|
message = response_info.get("message")
|
|
if isinstance(message, str) and len(message) > 300:
|
|
message = message[:300]
|
|
print(
|
|
"[auth-debug] register_response "
|
|
f"url={response_info.get('__url__')} status={response_info.get('__status__')} "
|
|
f"code={response_info.get('code')} message={message}",
|
|
flush=True,
|
|
)
|
|
try:
|
|
sonner = page.locator("[data-sonner-toast]")
|
|
if sonner.count() > 0:
|
|
html = sonner.first.evaluate("el => el.outerHTML.slice(0, 300)")
|
|
print(f"[auth-debug] sonner_toast={html}", flush=True)
|
|
except Exception as exc:
|
|
print(f"[auth-debug] sonner_toast_dump_failed: {exc}", flush=True)
|
|
|
|
|
|
def _is_already_registered(toast_text: str) -> bool:
|
|
text = (toast_text or "").lower()
|
|
return "already" in text and ("register" in text or "registered" in text)
|
|
|
|
|
|
def _wait_for_auth_not_loading(page, timeout_ms: int = 5000) -> None:
|
|
auth_status_selector = json.dumps(AUTH_STATUS)
|
|
page.wait_for_function(
|
|
"""
|
|
() => {
|
|
const status = document.querySelector(%s);
|
|
if (!status) return true;
|
|
return status.getAttribute('data-state') !== 'loading';
|
|
}
|
|
""" % auth_status_selector,
|
|
timeout=timeout_ms,
|
|
)
|
|
|
|
|
|
def step_01_open_login(
|
|
flow_page,
|
|
flow_state,
|
|
login_url,
|
|
active_auth_context,
|
|
step,
|
|
snap,
|
|
auth_debug_dump,
|
|
auth_click,
|
|
reg_email,
|
|
reg_email_generator,
|
|
reg_password,
|
|
reg_nickname,
|
|
reg_email_unique,
|
|
):
|
|
page = flow_page
|
|
with step("open login page"):
|
|
page.goto(login_url, wait_until="domcontentloaded")
|
|
flow_state["login_opened"] = True
|
|
snap("open")
|
|
|
|
|
|
def step_02_switch_to_register(
|
|
flow_page,
|
|
flow_state,
|
|
login_url,
|
|
active_auth_context,
|
|
step,
|
|
snap,
|
|
auth_debug_dump,
|
|
auth_click,
|
|
reg_email,
|
|
reg_email_generator,
|
|
reg_password,
|
|
reg_nickname,
|
|
reg_email_unique,
|
|
):
|
|
require(flow_state, "login_opened")
|
|
form, card = active_auth_context()
|
|
toggle_button = card.locator(REGISTER_TAB)
|
|
if toggle_button.count() == 0:
|
|
flow_state["register_toggle_available"] = False
|
|
pytest.skip("Register toggle not present; registerEnabled may be disabled.")
|
|
|
|
with step("switch to register"):
|
|
expect(toggle_button).to_have_count(1)
|
|
toggle_button.click()
|
|
flow_state["register_toggle_available"] = True
|
|
snap("toggled_register")
|
|
|
|
|
|
def step_03_submit_registration(
|
|
flow_page,
|
|
flow_state,
|
|
login_url,
|
|
active_auth_context,
|
|
step,
|
|
snap,
|
|
auth_debug_dump,
|
|
auth_click,
|
|
reg_email,
|
|
reg_email_generator,
|
|
reg_password,
|
|
reg_nickname,
|
|
reg_email_unique,
|
|
):
|
|
require(flow_state, "login_opened", "register_toggle_available")
|
|
page = flow_page
|
|
form, _ = active_auth_context()
|
|
nickname_input = form.locator(NICKNAME_INPUT)
|
|
if nickname_input.count() == 0:
|
|
pytest.skip("Register form not active; cannot submit registration.")
|
|
|
|
email_input = form.locator(EMAIL_INPUT)
|
|
password_input = form.locator(PASSWORD_INPUT)
|
|
|
|
current_email = reg_email
|
|
with step("fill registration form"):
|
|
expect(email_input).to_have_count(1)
|
|
expect(password_input).to_have_count(1)
|
|
nickname_input.fill(reg_nickname)
|
|
email_input.fill(current_email)
|
|
password_input.fill(reg_password)
|
|
expect(password_input).to_have_attribute("type", "password")
|
|
password_input.blur()
|
|
snap("filled")
|
|
|
|
retried = False
|
|
while True:
|
|
with step("submit registration and wait for response"):
|
|
form, _ = active_auth_context()
|
|
submit_button = form.locator(SUBMIT_BUTTON)
|
|
expect(submit_button).to_have_count(1)
|
|
if not retried:
|
|
snap("before_submit_click")
|
|
auth_debug_dump("before_submit_click", submit_button)
|
|
|
|
try:
|
|
response_info = capture_response_json(
|
|
page,
|
|
lambda: (
|
|
auth_click(
|
|
submit_button,
|
|
"submit_register_retry" if retried else "submit_register",
|
|
),
|
|
snap("retry_submitted" if retried else "submitted"),
|
|
),
|
|
lambda resp: resp.request.method == "POST"
|
|
and "/api/v1/users" in resp.url,
|
|
timeout_ms=RESULT_TIMEOUT_MS,
|
|
)
|
|
except PlaywrightTimeoutError as exc:
|
|
snap("failure")
|
|
raise AssertionError(
|
|
f"Register response not received in time. url={page.url} email={current_email}"
|
|
) from exc
|
|
|
|
_debug_register_response(page, response_info)
|
|
|
|
if response_info.get("code") == 0:
|
|
snap("registered_success_response")
|
|
form, _ = active_auth_context()
|
|
nickname_input = form.locator(NICKNAME_INPUT)
|
|
expect(nickname_input).to_have_count(0, timeout=RESULT_TIMEOUT_MS)
|
|
break
|
|
|
|
snap("registered_error_response")
|
|
message_text = response_info.get("message", "") or ""
|
|
if _is_already_registered(message_text) and not retried:
|
|
retried = True
|
|
with step("retry registration with new email"):
|
|
_wait_for_auth_not_loading(page)
|
|
form, _ = active_auth_context()
|
|
email_input = form.locator(EMAIL_INPUT)
|
|
expect(email_input).to_have_count(1)
|
|
current_email = reg_email_generator(force_unique=True)
|
|
email_input.fill(current_email)
|
|
snap("retry_filled")
|
|
continue
|
|
|
|
snap("failure")
|
|
raise AssertionError(
|
|
"Registration error detected. "
|
|
f"url={response_info.get('__url__')} status={response_info.get('__status__')} "
|
|
f"code={response_info.get('code')} message={response_info.get('message')} "
|
|
f"email={current_email}"
|
|
)
|
|
|
|
snap("success")
|
|
flow_state["register_complete"] = True
|
|
flow_state["registered_email"] = current_email
|
|
print(f"REGISTERED_EMAIL={current_email}", flush=True)
|
|
|
|
|
|
STEPS = [
|
|
("01_open_login", step_01_open_login),
|
|
("02_switch_to_register", step_02_switch_to_register),
|
|
("03_submit_registration", step_03_submit_registration),
|
|
]
|
|
|
|
|
|
@pytest.mark.p1
|
|
@pytest.mark.auth
|
|
@pytest.mark.parametrize("step_fn", flow_params(STEPS))
|
|
def test_register_success_optional_flow(
|
|
step_fn,
|
|
flow_page,
|
|
flow_state,
|
|
login_url,
|
|
active_auth_context,
|
|
step,
|
|
snap,
|
|
auth_debug_dump,
|
|
auth_click,
|
|
reg_email,
|
|
reg_email_generator,
|
|
reg_password,
|
|
reg_nickname,
|
|
reg_email_unique,
|
|
):
|
|
step_fn(
|
|
flow_page,
|
|
flow_state,
|
|
login_url,
|
|
active_auth_context,
|
|
step,
|
|
snap,
|
|
auth_debug_dump,
|
|
auth_click,
|
|
reg_email,
|
|
reg_email_generator,
|
|
reg_password,
|
|
reg_nickname,
|
|
reg_email_unique,
|
|
)
|