23

I am using Cucumber and Capybara. I need to make an HTTP DELETE request. Previously the features used webrat, so a simple statement like

visit "/comment/" + comment_id, :delete

worked, but now I am using Capybara.

The way to do a GET request is simply:

get 'path'

And to do a post request:

page.driver.post 'path'

But how can I simulate a DELETE request?

I found out that the driver Capybara is using is Capybara::RackTest::Driver, if that is any help.

I have also tried:

Capybara.current_session.driver.delete "/comments/" + comment_id

But that does not work.

Chris W.
  • 1,680
  • 16
  • 35
umar
  • 4,309
  • 9
  • 34
  • 47
  • 4
    Is there not a link or a button on your webpage that will trigger the deletion? Cucumber features are supposed to be a full-stack acceptance test. Your feature steps should take you through a happy flow of the application. You should follow links and press buttons and fill in data, etc, and then you could trigger that deletion simply by doing that. – MrDanA Feb 10 '12 at 14:20
  • what you are suggesting would be a preferred way to do so when i write new tests, but currently i am migrating from webrat to capybara. i dont want to modify the 30 or so tests by rewriting them, i want to first make them work using capybara, and then may be rewrite those later as needed. – umar Feb 10 '12 at 14:27
  • 1
    really weird, because those methods are included in tha RackTest driver: https://github.com/jnicklas/capybara/blob/master/lib/capybara/rack_test/driver.rb#L78 – apneadiving Feb 10 '12 at 15:22

5 Answers5

30
page.driver.submit :delete, "/comments/#{comment_id}", {}

Documentation at: http://rubydoc.info/gems/capybara/1.1.2/Capybara/RackTest/Browser:submit

1000 Needles
  • 309
  • 3
  • 3
  • 7
    It's worth mentioning that this method is unique to the RackTest driver, so this won't work in Selenium or Capybara-webkit. – 1000 Needles Jul 23 '12 at 01:43
  • 2
    This works just fine for capybara-webkit (and I suspect it does for selenium, as well). – Brad Werth Aug 06 '13 at 22:27
  • +1 thank god for this. It's certainly not a technique that you need when testing "happy paths", but this is making it miles easier for me to test access control & ensure my before filters are working properly. – Topher Hunt Feb 09 '14 at 01:03
6

In case you are using a driver that doesn't support custom HTTP requests (e.g. Capybara-webkit, see closed issue and current driver), you could just temporary switch to RackTest driver to submit your request.

For example:

# Submit an HTTP delete request, using rack_test driver
def http_delete path
  current_driver = Capybara.current_driver
  Capybara.current_driver = :rack_test
  page.driver.submit :delete, path, {}
  Capybara.current_driver = current_driver
end
mrmrf
  • 99
  • 1
  • 4
  • This solution is much better. Who knows what driver you'll use in future. – Anton Chikin Apr 28 '14 at 14:35
  • 1
    This solution has one (potentially) big problem: It will not retain any session or cookie information from previous requests. So, e.g., if you need to make a `DELETE` request as an authenticated user, this code will probably not work (unless you're already using the `:rack_test` driver). See [my proposed solution](https://stackoverflow.com/a/36580783/119363) for a method that will retain session info. – Chris W. Apr 12 '16 at 18:03
2
page.driver.delete("/comments/#{comment_id}")

Use delete works fine. No need to use the lower level submit method for this.

PS: tested with capybara-webkit 1.6.0 and capybara 2.4.4

Olivier Lacan
  • 2,636
  • 2
  • 24
  • 22
1

If you need a solution that will retain session information (e.g., if you need your DELETE request to be issued as an authenticated user), and that will work with both the :rack_test driver and the :webkit driver (assuming you have jQuery available in your app), try something like this:

def manual_http_request(method, path)
  if Capybara.current_driver == :rack_test
    page.driver.submit method, path, {}
  else
    page.execute_script("$.ajax({url: '#{path}', data: {}, type: '#{method}'});")
    # Wait for the request to finish executing...
    Timeout.timeout(10) { loop until page.evaluate_script('jQuery.active').zero? }
  end
end

I suspect this will also work with the :selenium driver but have not tested it. Maybe someone else can chime in on that.

Chris W.
  • 1,680
  • 16
  • 35
0

This works in selenium. It's assuming you have a logout button/link on any page it is called on

Instead of:

visit '/logout'

Do:

def logout_user
  logout_link = page.first('#my_css_selector')
  logout_link.click if logout_link
end

#... elsewhere in a step
logout_user
Cory ODaniel
  • 708
  • 6
  • 9