Rspec/Capybara/Poltergeist: How to stub the URL? - ruby-on-rails

For my tests, I would like to stub/fake the URL that the tests are using, so that in Rails my request.host shows up as something besides 'www.example.com'
In my research, most people suggest changing capybara's app_host, but this only works when you're using the default driver. When using Poltergeist it will actually go to that URL.
Is there anything else I can use?

When using Capybara/Poltergeist you're doing feature tests that test the full stack. You should not be attempting to stub/mock anything when doing that (maybe you want a controller spec instead?).
Additionally when not using the default rack-test driver you should be seeing '127.0.0.1' for request.host in your app (not www.example.com). You can change the Capybara.app_host to anything you want, but would then to need make sure your local DNS resolved that to 127.0.0.1 (/etc/hosts entry, etc). Most CI systems also allow you to specify DNS resolutions - https://circleci.com/docs/configuration/#hosts

Related

Rspec, Capybara and Poltergiest: Failed to reach server only when js: true is enabled

I have working integration/feature tests using Rails 4, rspec-rails 3.6, and capybara 2.18. I am trying to get these tests to pass on pages that are being rewritten in a javascript framework (React). To do so, I need to use a driver such as Poltergeist instead of the default headless browser so that javascript can run on the test pages.
I've installed poltergeist 1.18. I add Capybara.javascript_driver = :poltergeist, no problem. The tests pass with the old (html-only) pages.
However just adding js: true to an existing, working test (that is, before changing the rendered page to to the new version) and the test fails with
Request to 'http://account.example.com/user/sessions/new' failed to reach server, check DNS and/or server status
Note that this url is using the account subdomain in our application, and this may be related to the issue.
Here is one of the failing tests, isolated.
feature 'Vistor tries to login', js: true do
scenario 'with valid login', :with_project_access_user do
visit new_user_sessions_url
fill_in 'user_session[login]', with: 'test#gmail.com'
fill_in 'user_session[password]', with: 'mysweetpassword'
click_button 'Log In'
expect(current_url).to match('http://account\.')
end
end
It's failing on the first line: visit new_user_sessions_url. This test passes if I remove js" true. In both cases, new_user_sessions_url is "http://account.example.com/user/sessions/new"
Using new_user_sessions_path instead of new_user_sessions_url doesn't work here because the subdomain is a required part of the route. If there is a way around the subdomain matching (without removing the subdomains -- they are used in our production app) that's a totally acceptable solution.
Since the test works without js: true, I have to assume that Capybara is doing something behind the scenes to handle the example.com domain. But how does adding js: true break this? And how can I fix this to get my existing tests to pass?
When not using js: true Capybara is using the rack_test driver which completely ignores hostnames, so the only thing that matters is the path. When you use js: true real hostnames are being used because the "browser" is querying them to figure out where to make its requests. It is your responsibility to make sure whatever hostname your app is generating urls with resolves to an ip the app is listening on. Easiest solution for that is to add entries to /etc/hosts mapping the domain/subdomain names you're using for testing to 127.0.0.1.
Note: Poltergeist uses PhantomJS which is equivalent to an 8-9 year old browser at this point. This means you're most likely going to run into issues trying to run React apps on it, and you should really look into upgrading to a driver for modern browser - Selenium, Apparition, etc.

RSpec/Capybara: Cross-Domain JS Testing

I'm working on an advertising application, where there is some cross-domain JS. I'd like to write tests that verify that the cross-domain JS is working as intended.
My first thoughts are that I would need to be able to
visit some_url_thats_not_my_rails_app
However, Capybara throws a "No Route Matches", since it obviously goes to only relative paths, and is intended for testing your OWN website. But I really need to go on ANOTHER page, and verify that things like the serialized token are identical.
Is Capybara the right tool for this? If so, what do I need to do to force non-relative paths?
Yes, Capybara is right tool for it. You can also use Ruby with Selenium webdriver(Would be awesome with page object gem) or Ruby with Watir webdriver.
To visit and test any web application, you can use Ruby and Capybara. For this you need to set app_host. Add this one in support/env.rb:
Capybara.app_host = "http://flipkart.com"
And in hooks.rb
Before do
visit('/')
end
You can use Capybara with Selenium webdriver and you will be good to test any deployed application in production, staging or prep env.

Serve Static File in Cucumber Test from an HTTP location

I'm testing a JSON API for a model with a Carrierwave Uploader, but I'm only allowing uploads via a remote_url, so I need some way to get an HTTP address for a file in my public assets, since I know those'll always be around.
To put it another way:
When the Rails server is running, I have a static asset at "http://localhost:3000/assets/logo.png". I need the address of that file while Cucumber is running - aka, I need to serve a static file while running Cucumber, so that a different part of the Rails app can "download" that file.
Edit: These test are not run with a browser, although (as of recently), they are run with a session. I may be using Cucumber, but I have written no code to use or start Capybara.
If I'm understanding your question correctly. You may want to look into something like Fakeweb or Webmock to serve back/mock external requests.
I think you can get the root uri for the test server that cucumber starts by using these:
For capybara < 2.0:
Capybara.current_session.driver.rack_server.host
Capybara.current_session.driver.rack_server.port
Capybara 2.0:
Capybara.current_session.server.host
Capybara.current_session.server.port

Rails: How to set port number for the asset hosts in development

Rails lets me set multiple asset hosts as an application configuration. Problem is, I've been writing the full host as an argument. That is, in development I've been appending the ":3000" to it. Which has worked fine, until the day I had to run 2 projects in parallel, and one of them add to go to a different port (let's say, 3001). As I already read, Rails app can't possibly know which port the web server is listening to. Is there a workaround for this?
If I understand your question, you can use asset_path helper instead of the full path by hand, and put different asset_hosts in production.rb and development.rb.
For the port issue, inside a controller method you can get it from the request, but, outside, I don't know a way to do that... you can try the PORT enviroment variable.
Hope it helps.

Rails, Cucumber, Capybara, Selenium, Pow, domains, subdomains and javascript

TL:DR - How can I make Cucumber request a page through the app but pretend that the request came from "http://mysubdomain.mydomain.dev", and run the javascript properly?
Ok, this may take a while to explain, but please bear with me...
I'm a 'relatively' experienced Rails dev, but only recently took the plunge into full BDD/TDD.
The page I'm testing in cucumber has some javascript in it for creating a new nested object (very similar to this RailsCast).
Now the problem lies in the fact that not only do I have variable sub-domains, I have variable Domains too (Just trust me on that one...)
So the app needs to query the request.host to find the current_domain and the current_subdomain before everything ties up in the background and it can carry on serving the right bits of the app.
I've managed to get all of the tests passing fine using the host! domain and Capybara.default_host = domain techniques in the background steps.
However, when I use the #javascript tag on the feature that tests a page with .js on it, I get Firefox grabbing focus, then trying to load the full url.
Now it also happens that I'm running Pow, and have those urls hooked up to the dev server. Unsurprisingly, they don't work as when the user tries to log in, it's looking in the dev DB. I tried running the suite after turning the pow server off, and it just timed out.
Surely the javascript webdriver shouldn't be actually accessing the url, but simply running the app itself and pretending that the host is what I tell it to be???
I'm obviously missing something here - How can I make Cucumber build the page internally, but pretend that the request came from "http://mysubdomain.mydomain.dev"?
edit: Jason - the variable domain trick is achieved exactly the same as subdomains... If you can query the db for an account based on request.subdomains.first, you can query for a domain through request.domain. You just have to double check a few things like capitalisation etc. to minimize the risk of a malformed url breaking stuff, and obviously you need to ensure that the domain record exists in the db first...
Oh - and cache the domain record requests with care...
It means that you can serve the same app, but with different styling and landing pages etc. Useful for PaaS apps with a broad customer base - you can re-brand it and sell it as a specific solution to one group's problems, even though it's the same guts underneath.
I am looking to do something very similar. I thought about adding an additional pow directory for test, then using the pow directives to override the environment. I think this is done within a ".powenv" inside your app directory. Here is an example of a quick fix:
echo export RAILS_ENV=cucumber > .powenv && touch tmp/restart.txt
But it would be better to do something dynamic so that before you start the test, you can tell pow what env to run, then when finished switch back, maybe even run the test server on a different port on a temp basis. Pow is the only great solution I know of so far for handeling subdomains.
EDITED: I have this working now in my environment with the following addition to my features/support/env.rb file.
# Switch Pow to For Cucumber Tests
Capybara.default_driver = :selenium # Subdomain testing will only work with pow and selenium
pow_config = "#{Rails.root}/.powenv" # Dont change, this is the Config Files Location.
pow_config_stash = "#{Rails.root}/.powenv_original" # This is what the config will be stashed as during testing.
Before do
# Set the default host
Capybara.app_host = "http://www.resipsa.dev"
# Stash the existing config
File.rename(pow_config,pow_config_stash) if File.exists? pow_config
# Write the new pow config
f = File.new("#{Rails.root}/.powenv", "w")
f.write "export RAILS_ENV=test"
f.close
# Touch tmp/restart.txt to force a restart
FileUtils.touch "#{Rails.root}/tmp/restart.txt"
end
After do
# Delete the temp config
File.delete(pow_config)
# Restore the Original Config
File.rename(pow_config_stash,pow_config) if File.exists? pow_config_stash
# Touch tmp/restart.txt to force a restart
FileUtils.touch "#{Rails.root}/tmp/restart.txt"
end

Resources