Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion tests/playwright/pages/mcp_registry_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ def clear_filters(self) -> None:
with self.page.expect_response("**/admin/mcp-registry/partial**", timeout=5000):
self.category_filter.select_option("")

# Wait for HTMX swap to complete and DOM to settle
self.wait_for_registry_results_ready(timeout=10000)

def click_category_badge(self, category: str) -> None:
"""Click on a category badge to filter by that category.

Expand Down Expand Up @@ -328,7 +331,19 @@ def get_server_count(self) -> int:
"""
# Wait for grid to be stable before counting
self.page.wait_for_selector("#server-grid", state="attached", timeout=10000)
self.page.wait_for_timeout(300) # Brief wait for any in-flight HTMX swaps

# Wait for HTMX requests to complete (no .htmx-request class on body or forms)
self.page.wait_for_function(
"""() => {
const hasHtmxRequest = document.body.classList.contains('htmx-request');
const forms = document.querySelectorAll('form.htmx-request');
return !hasHtmxRequest && forms.length === 0;
}""",
timeout=10000,
)

# Brief wait for DOM rendering after HTMX swap
self.page.wait_for_timeout(300)
return self.server_cards.count()

def get_total_servers_count(self) -> int:
Expand Down
16 changes: 14 additions & 2 deletions tests/playwright/test_api_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,20 @@ def test_should_handle_object_parameter_validation(self, page: Page, admin_page,
# Row actions live inside an Alpine overflow menu — open the ⋮ trigger first.
tool_row.locator("button[aria-expanded]").click()
tool_row.locator('[role="menu"]').wait_for(state="visible", timeout=5000)
test_btn = tool_row.locator('button:has-text("Test")')
test_btn.click()

# HTMX may swap the tools-table-body, detaching the button during click.
# Extract tool ID and call testTool() directly via page.evaluate() to avoid DOM detachment.
tool_id = tool_row.evaluate("el => el.querySelector('button[data-test-tool-id]')?.getAttribute('data-test-tool-id')")
if tool_id:
# Call testTool directly to avoid HTMX DOM detachment race condition
# Tool IDs are UUID strings, not integers - pass as quoted string
import json

page.evaluate(f"Admin.testTool({json.dumps(tool_id)})")
else:
# Fallback if data attribute is missing
test_btn = tool_row.locator('button:has-text("Test")')
test_btn.click()

expect(page.locator("#tool-test-modal")).to_be_visible(timeout=10000)
page.wait_for_selector("#tool-test-form-fields", state="visible", timeout=10000)
Expand Down
Loading