diff --git a/test/system/property_test.rb b/test/system/property_test.rb index c6a5f17b8..19941144f 100644 --- a/test/system/property_test.rb +++ b/test/system/property_test.rb @@ -23,13 +23,33 @@ class PropertiesEditTest < ApplicationSystemTestCase # (`turbo_refreshes_with method: :morph` reacting to a family-stream # broadcast). If the edit modal is opened while that refresh is in flight, # the morph re-renders the page and wipes the just-loaded `#modal` - # turbo-frame before the dialog is interactive. Open via the account menu and - # retry once the refresh has settled so the test is deterministic instead of + # turbo-frame before the dialog is interactive — and can detach the menu + # node mid-click ("Node with given id does not belong to the document"), + # which Capybara does not auto-retry. Open via the account menu and retry + # until the edit form is present so the test is deterministic instead of # racing the broadcast. def open_account_edit_dialog 3.times do - find("[data-testid='account-menu']").click - click_on "Edit" + # A prior (slow) attempt may have already opened the edit form. + return if has_selector?("#account_accountable_attributes_subtype", wait: 0) + + begin + within_testid("account-menu") do + # Open the menu only when it's closed. DS::Menu's trigger toggles + # (menu_controller#toggle), so blindly re-clicking an already-open + # menu would close it and hide "Edit", turning a slow-but-successful + # modal load into a fresh flake. + unless has_selector?("[role='menu']", visible: true, wait: 0) + find("button").click + end + click_on "Edit" + end + rescue Selenium::WebDriver::Error::WebDriverError => e + raise unless e.message.match?( + /does not belong to the document|stale element reference/i, + ) + next + end return if has_selector?("#account_accountable_attributes_subtype", wait: 2) end assert_selector "#account_accountable_attributes_subtype"