From 505c0cee6b4917d035980bcaf9506e2cbc6f6ebf Mon Sep 17 00:00:00 2001 From: Mike Pack Date: Mon, 1 Apr 2013 16:41:55 -0600 Subject: [PATCH 1/4] Add modal API --- README.md | 38 +++++++++++++ lib/capybara/driver/base.rb | 8 +++ lib/capybara/selenium/driver.rb | 21 +++++++ lib/capybara/session.rb | 56 ++++++++++++++++++- lib/capybara/spec/public/test.js | 26 +++++++++ .../spec/session/accept_alert_spec.rb | 35 ++++++++++++ .../spec/session/accept_confirm_spec.rb | 19 +++++++ .../spec/session/accept_prompt_spec.rb | 19 +++++++ .../spec/session/dismiss_confirm_spec.rb | 19 +++++++ .../spec/session/dismiss_prompt_spec.rb | 19 +++++++ .../spec/session/respond_to_prompt_spec.rb | 19 +++++++ lib/capybara/spec/views/with_js.erb | 16 ++++++ spec/dsl_spec.rb | 1 + spec/rack_test_spec.rb | 1 + 14 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 lib/capybara/spec/session/accept_alert_spec.rb create mode 100644 lib/capybara/spec/session/accept_confirm_spec.rb create mode 100644 lib/capybara/spec/session/accept_prompt_spec.rb create mode 100644 lib/capybara/spec/session/dismiss_confirm_spec.rb create mode 100644 lib/capybara/spec/session/dismiss_prompt_spec.rb create mode 100644 lib/capybara/spec/session/respond_to_prompt_spec.rb diff --git a/README.md b/README.md index 9c383ef2b..e93836622 100644 --- a/README.md +++ b/README.md @@ -482,6 +482,44 @@ that this may break with more complicated expressions: result = page.evaluate_script('4 + 4'); ``` +### Modals + +In drivers which support it, you can accept, dismiss and respond to alerts, confirms and prompts. + +You can accept or dismiss alert messages by wrapping the code that produces an alert in a block: + +```ruby +accept_alert do + click_link('Show Alert') +end +``` + +You can accept or dismiss a confirmation by wrapping it in a block, as well: + +```ruby +dismiss_confirm do + click_link('Show Confirm') +end +``` + +You can accept or dismiss prompts as well. Additionally, you can respond to a prompt: + +```ruby +respond_to_prompt 'Linus Torvalds' do + click_link('Show Prompt About Linux') +end +``` + +All modal methods return the message that was presented. So, you can access the prompt message +by assigning the return to a variable: + +```ruby +message = respond_to_prompt 'Linus Torvalds' do + click_link('Show Prompt About Linux') +end +expect(message).to eq('Who is the chief architect of Linux?') +``` + ### Debugging It can be useful to take a snapshot of the page as it currently is and take a diff --git a/lib/capybara/driver/base.rb b/lib/capybara/driver/base.rb index 9fd20d0f0..73a24583c 100644 --- a/lib/capybara/driver/base.rb +++ b/lib/capybara/driver/base.rb @@ -55,6 +55,14 @@ def within_window(handle) raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#within_window' end + def accept_modal(type, options={}, &blk) + raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#accept_modal' + end + + def dismiss_modal(type, &blk) + raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#dismiss_modal' + end + def invalid_element_errors [] end diff --git a/lib/capybara/selenium/driver.rb b/lib/capybara/selenium/driver.rb index 73ae49384..94b9dad2a 100644 --- a/lib/capybara/selenium/driver.rb +++ b/lib/capybara/selenium/driver.rb @@ -146,6 +146,21 @@ def within_window(selector, &blk) browser.switch_to.window(handle, &blk) end + def accept_modal(type, options={}, &blk) + yield + modal.send_keys options[:response] if options[:response] + message = modal.text + modal.accept + message + end + + def dismiss_modal(type, &blk) + yield + message = modal.text + modal.dismiss + message + end + def quit @browser.quit if @browser rescue Errno::ECONNREFUSED @@ -158,4 +173,10 @@ def invalid_element_errors [Selenium::WebDriver::Error::StaleElementReferenceError, Selenium::WebDriver::Error::UnhandledError, Selenium::WebDriver::Error::ElementNotVisibleError] end +private + + def modal + @browser.switch_to.alert + end + end diff --git a/lib/capybara/session.rb b/lib/capybara/session.rb index 5daf340fe..4b7bb935c 100644 --- a/lib/capybara/session.rb +++ b/lib/capybara/session.rb @@ -45,7 +45,11 @@ class Session :save_and_open_screenshot, :reset_session!, :response_headers, :status_code, :title, :has_title?, :has_no_title?, :current_scope ] - DSL_METHODS = NODE_METHODS + SESSION_METHODS + MODAL_METHODS = [ + :accept_alert, :accept_confirm, :dismiss_confirm, :accept_prompt, + :dismiss_prompt, :respond_to_prompt + ] + DSL_METHODS = NODE_METHODS + SESSION_METHODS + MODAL_METHODS attr_reader :mode, :app, :server attr_accessor :synchronized @@ -367,6 +371,56 @@ def evaluate_script(script) driver.evaluate_script(script) end + ## + # + # Execute the block, accepting a alert. + # + def accept_alert(&blk) + driver.accept_modal(:alert, &blk) + end + + ## + # + # Execute the block, accepting a confirm. + # + def accept_confirm(&blk) + driver.accept_modal(:confirm, &blk) + end + + ## + # + # Execute the block, dismissing a confirm. + # + def dismiss_confirm(&blk) + driver.dismiss_modal(:confirm, &blk) + end + + ## + # + # Execute the block, accepting a prompt. + # + def accept_prompt(&blk) + driver.accept_modal(:prompt, &blk) + end + + ## + # + # Execute the block, dismissing a prompt. + # + def dismiss_prompt(&blk) + driver.dismiss_modal(:prompt, &blk) + end + + ## + # + # Execute the block, responding with the provided value. + # + # @param [String] response Response to provide to the prompt + # + def respond_to_prompt(response, &blk) + driver.accept_modal(:prompt, {response: response}, &blk) + end + ## # # Save a snapshot of the page. diff --git a/lib/capybara/spec/public/test.js b/lib/capybara/spec/public/test.js index e5db4d30b..3f5df31f7 100644 --- a/lib/capybara/spec/public/test.js +++ b/lib/capybara/spec/public/test.js @@ -67,4 +67,30 @@ $(function() { e.preventDefault(); $(this).after('Has been right clicked'); }); + $('#open-alert').click(function() { + alert('Alert opened'); + $(this).attr('opened', 'true'); + }); + $('#open-delayed-alert').click(function() { + var link = this; + setTimeout(function() { + alert('Delayed alert opened'); + $(link).attr('opened', 'true'); + }, 250); + }); + $('#open-confirm').click(function() { + if(confirm('Confirm opened')) { + $(this).attr('confirmed', 'true'); + } else { + $(this).attr('confirmed', 'false'); + } + }); + $('#open-prompt').click(function() { + var response = prompt('Prompt opened'); + if(response === null) { + $(this).attr('response', 'dismissed'); + } else { + $(this).attr('response', response); + } + }); }); diff --git a/lib/capybara/spec/session/accept_alert_spec.rb b/lib/capybara/spec/session/accept_alert_spec.rb new file mode 100644 index 000000000..54a0ec9ed --- /dev/null +++ b/lib/capybara/spec/session/accept_alert_spec.rb @@ -0,0 +1,35 @@ +Capybara::SpecHelper.spec '#accept_alert', :requires => [:modals] do + before do + @session.visit('/with_js') + end + + it "should accept the alert" do + @session.accept_alert do + @session.click_link('Open alert') + end + expect(@session).to have_xpath("//a[@id='open-alert' and @opened='true']") + end + + it "should return the message presented" do + message = @session.accept_alert do + @session.click_link('Open alert') + end + expect(message).to eq('Alert opened') + end + + context "with an asynchronous alert" do + it "should accept the alert" do + @session.accept_alert do + @session.click_link('Open delayed alert') + end + expect(@session).to have_xpath("//a[@id='open-delayed-alert' and @opened='true']") + end + + it "should return the message presented" do + message = @session.accept_alert do + @session.click_link('Open delayed alert') + end + expect(message).to eq('Delayed alert opened') + end + end +end \ No newline at end of file diff --git a/lib/capybara/spec/session/accept_confirm_spec.rb b/lib/capybara/spec/session/accept_confirm_spec.rb new file mode 100644 index 000000000..be9cb8c96 --- /dev/null +++ b/lib/capybara/spec/session/accept_confirm_spec.rb @@ -0,0 +1,19 @@ +Capybara::SpecHelper.spec '#accept_confirm', :requires => [:modals] do + before do + @session.visit('/with_js') + end + + it "should accept the confirm" do + @session.accept_confirm do + @session.click_link('Open confirm') + end + expect(@session).to have_xpath("//a[@id='open-confirm' and @confirmed='true']") + end + + it "should return the message presented" do + message = @session.accept_confirm do + @session.click_link('Open confirm') + end + expect(message).to eq('Confirm opened') + end +end \ No newline at end of file diff --git a/lib/capybara/spec/session/accept_prompt_spec.rb b/lib/capybara/spec/session/accept_prompt_spec.rb new file mode 100644 index 000000000..85f1cd4ff --- /dev/null +++ b/lib/capybara/spec/session/accept_prompt_spec.rb @@ -0,0 +1,19 @@ +Capybara::SpecHelper.spec '#accept_prompt', :requires => [:modals] do + before do + @session.visit('/with_js') + end + + it "should accept the prompt with no message" do + @session.accept_prompt do + @session.click_link('Open prompt') + end + expect(@session).to have_xpath("//a[@id='open-prompt' and @response='']") + end + + it "should return the message presented" do + message = @session.accept_prompt do + @session.click_link('Open prompt') + end + expect(message).to eq('Prompt opened') + end +end \ No newline at end of file diff --git a/lib/capybara/spec/session/dismiss_confirm_spec.rb b/lib/capybara/spec/session/dismiss_confirm_spec.rb new file mode 100644 index 000000000..9c91a50de --- /dev/null +++ b/lib/capybara/spec/session/dismiss_confirm_spec.rb @@ -0,0 +1,19 @@ +Capybara::SpecHelper.spec '#dismiss_confirm', :requires => [:modals] do + before do + @session.visit('/with_js') + end + + it "should dismiss the confirm" do + @session.dismiss_confirm do + @session.click_link('Open confirm') + end + expect(@session).to have_xpath("//a[@id='open-confirm' and @confirmed='false']") + end + + it "should return the message presented" do + message = @session.dismiss_confirm do + @session.click_link('Open confirm') + end + expect(message).to eq('Confirm opened') + end +end \ No newline at end of file diff --git a/lib/capybara/spec/session/dismiss_prompt_spec.rb b/lib/capybara/spec/session/dismiss_prompt_spec.rb new file mode 100644 index 000000000..331adede7 --- /dev/null +++ b/lib/capybara/spec/session/dismiss_prompt_spec.rb @@ -0,0 +1,19 @@ +Capybara::SpecHelper.spec '#dismiss_prompt', :requires => [:modals] do + before do + @session.visit('/with_js') + end + + it "should dismiss the prompt" do + @session.dismiss_prompt do + @session.click_link('Open prompt') + end + expect(@session).to have_xpath("//a[@id='open-prompt' and @response='dismissed']") + end + + it "should return the message presented" do + message = @session.dismiss_prompt do + @session.click_link('Open prompt') + end + expect(message).to eq('Prompt opened') + end +end \ No newline at end of file diff --git a/lib/capybara/spec/session/respond_to_prompt_spec.rb b/lib/capybara/spec/session/respond_to_prompt_spec.rb new file mode 100644 index 000000000..b12ea3c4a --- /dev/null +++ b/lib/capybara/spec/session/respond_to_prompt_spec.rb @@ -0,0 +1,19 @@ +Capybara::SpecHelper.spec '#respond_to_prompt', :requires => [:modals] do + before do + @session.visit('/with_js') + end + + it "should accept the prompt" do + @session.respond_to_prompt 'the response' do + @session.click_link('Open prompt') + end + expect(@session).to have_xpath("//a[@id='open-prompt' and @response='the response']") + end + + it "should return the message presented" do + message = @session.respond_to_prompt 'the response' do + @session.click_link('Open prompt') + end + expect(message).to eq('Prompt opened') + end +end \ No newline at end of file diff --git a/lib/capybara/spec/views/with_js.erb b/lib/capybara/spec/views/with_js.erb index d07a3884a..e97cfbcb9 100644 --- a/lib/capybara/spec/views/with_js.erb +++ b/lib/capybara/spec/views/with_js.erb @@ -72,6 +72,22 @@

Click me

+

+ Open alert +

+ +

+ Open delayed alert +

+ +

+ Open confirm +

+ +

+ Open prompt +

+