From 1692d8d6738ac0d1ea57053ba29fc2aee3d96878 Mon Sep 17 00:00:00 2001 From: Sean Doyle Date: Thu, 23 Jul 2026 14:38:31 -0400 Subject: [PATCH] Introduce `raise_on_unhandled_modal` browser configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The problem --- When executing a system test suite that relies on `confirm`, `prompt`, or other browser-level modals, the default behavior to ignore modally presented dialogs can cause false negatives. For example, a change to the implementation might accidentally introduce a perpetually prompting confirmation modal. While the test suite outputs "Modal window … has been opened" warnings, the underlying test still passes. The proposal --- This commit proposes a new Cuprite-level `:raise_on_unhandled_modal` option to control whether an unhandled modal warns, or raises. When set to `true`, then false negative test would fail, rather than pass. --- README.md | 1 + lib/capybara/cuprite/browser.rb | 9 +++++++++ lib/capybara/cuprite/driver.rb | 1 + lib/capybara/cuprite/options.rb | 2 +- lib/capybara/cuprite/page.rb | 6 +++++- spec/features/session_spec.rb | 12 ++++++++++++ 6 files changed, 29 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c6b6e376..7c914d1f 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ end `Cuprite`-specific options are: * options `Hash` + * `:raise_on_unhandled_modal` (Boolean) - When set to `false`, output a warning. When set to `true`, raise an exception * `:url_blacklist` (Array) - array of regexes to match against requested URLs * `:url_whitelist` (Array) - array of regexes to match against requested URLs diff --git a/lib/capybara/cuprite/browser.rb b/lib/capybara/cuprite/browser.rb index 9d04f44c..a1283753 100644 --- a/lib/capybara/cuprite/browser.rb +++ b/lib/capybara/cuprite/browser.rb @@ -14,6 +14,7 @@ class Browser < Ferrum::Browser def initialize(options = nil) super + @options.raise_on_unhandled_modal = options&.delete(:raise_on_unhandled_modal) @options.url_blacklist = prepare_wildcards(options&.dig(:url_blacklist)) @options.url_whitelist = prepare_wildcards(options&.dig(:url_whitelist)) @@ -49,6 +50,14 @@ def resize(**options) super end + def raise_on_unhandled_modal + @options.raise_on_unhandled_modal + end + + def raise_on_unhandled_modal=(value) + @options.raise_on_unhandled_modal = value + end + def url_whitelist @options.url_whitelist end diff --git a/lib/capybara/cuprite/driver.rb b/lib/capybara/cuprite/driver.rb index 8eaed549..54e72867 100644 --- a/lib/capybara/cuprite/driver.rb +++ b/lib/capybara/cuprite/driver.rb @@ -137,6 +137,7 @@ def reset! @paper_size = nil browser.url_blacklist = @options[:url_blacklist] browser.url_whitelist = @options[:url_whitelist] + browser.raise_on_unhandled_modal = @options.fetch(:raise_on_unhandled_modal, false) browser.reset @started = false end diff --git a/lib/capybara/cuprite/options.rb b/lib/capybara/cuprite/options.rb index 1f863dee..40a124c7 100644 --- a/lib/capybara/cuprite/options.rb +++ b/lib/capybara/cuprite/options.rb @@ -4,7 +4,7 @@ module Ferrum class Browser class Options attr_writer :window_size - attr_accessor :url_blacklist, :url_whitelist + attr_accessor :url_blacklist, :url_whitelist, :raise_on_unhandled_modal def reset_window_size @window_size = @options[:window_size] diff --git a/lib/capybara/cuprite/page.rb b/lib/capybara/cuprite/page.rb index 2b21a863..2fefc9c6 100644 --- a/lib/capybara/cuprite/page.rb +++ b/lib/capybara/cuprite/page.rb @@ -162,10 +162,14 @@ def prepare_page response = @modal_response || params["defaultPrompt"] else with_text = params["message"] ? "with text `#{params['message']}` " : "" - warn "Modal window #{with_text}has been opened, but you didn't wrap " \ + message = + "Modal window #{with_text}has been opened, but you didn't wrap " \ "your code into (`accept_prompt` | `dismiss_prompt` | " \ "`accept_confirm` | `dismiss_confirm` | `accept_alert`), " \ "accepting by default" + + @options.raise_on_unhandled_modal ? raise(message) : warn(message) + options = { accept: true } response = params["defaultPrompt"] end diff --git a/spec/features/session_spec.rb b/spec/features/session_spec.rb index a44aea1b..651ad07c 100644 --- a/spec/features/session_spec.rb +++ b/spec/features/session_spec.rb @@ -1110,6 +1110,18 @@ expect(@session).to have_xpath("//a[@id='open-match' and @confirmed='true']") end + it "configured to raise warning" do + @session.driver.browser.raise_on_unhandled_modal = true + + @session.visit "/cuprite/with_js" + + expect { @session.click_link("Open for match") }.to raise_error( + "Modal window with text `{T}ext \\w|th [reg.exp] (chara©+er$)?` has been opened, " \ + "but you didn't wrap your code into (`accept_prompt` | `dismiss_prompt` | `accept_confirm` " \ + "| `dismiss_confirm` | `accept_alert`), accepting by default" + ) + end + it "matches on partial strings" do @session.visit "/cuprite/with_js" expect do