Reputation: 16793
I'm using the Mobvious gem in a Rails project to determine the kind of device being used to access a page. The user story I'm having issues with is:
desktop
value is put into the session to make sure their preference for viewing the desktop site on their mobile device is recorded (when mobile users first access the home page on a mobile device, they get automatically re-directed to the mobile site)The code to implement the conditional rendering with Mobvious is trivial:
app/views/welcome/index.html.haml
- for_device_type :mobile do
.switch-to-mobile-site-banner
# code for banner
The code itself works as expected: the issue is that I want to write a RSpec feature test for this, but I think I can't seem to simulate a mobile user agent properly in a RSpec feature test scenario where I don't have direct access to the request
object. My rationale is that a feature spec is more appropriate here than a request spec as I am testing for the presence of specific content on a page.
So far, taking cues from the helper specs of the Mobvious gem, I've stubbed out a request
object in my feature spec that sets device_type
to :mobile
as I want, but I can't seem to get the content in the .switch-to-mobile-site-banner
to display:
spec/features/mobile_navigation_features_spec.rb
feature 'Switch to mobile site banner' do
include Mobvious::Rails::Helper
given(:env) { double('env') }
given(:request) { double('request') }
background do
allow(env).to receive(:[]).with('mobvious.device_type').and_return(:mobile)
allow(request).to receive(:env).and_return(env)
end
scenario 'mobile user prefers using the desktop site' do
visit root_url(desktop: 1)
puts "Device type is #{device_type}" # => correctly returns :mobile
expect(page).to have_selector('.switch-to-mobile-site-banner') # fails
end
end
The expectation is failing, and using save_and_open_page
to look at what's rendering on the page shows no banner, and I don't know why. I don't seem to be getting any errors when the code runs through Mobvious' for_device_type
method, so I'm thinking perhaps there's something I'm missing in the request
object mocking(?).
I'm not tied specifically to any implementation/test-type, so I'm open to any other ideas on how I can test the functionality I want to. I thought initially the rack_session_access
gem could help out, but unless I'm using it wrong, I couldn't use it to do what I want.
Clarification: I am using Poltergeist for Capybara's javascript driver.
I think what really threw me with this problem is that in the mobvious-rails gem, when you want to get the device_type
, it calls the request
object directly and I figured I needed to mock this out in order to get the helper working properly in my specs. As pointed out in the answers, this kind of low-level mocking in a high-level feature spec is a code (spec?) smell, and in hindsight I should have listened to the code practically telling me I was doing it wrong. Also, for some reason, adding js: true
in this case didn't even cross my mind, regardless of having used it in other tests of mine.
I ended up using parts of both Billy Chan and Kaleidoscope's answers to formulate a solution I like that is much simpler and cleaner:
spec/features/mobile_navigation_features_spec.rb
feature 'Switch to mobile site banner' do
background do
page.driver.headers = { "User-Agent" => "mobile" }
end
scenario 'mobile user prefers using the desktop site', js: true do
visit root_path(desktop: 1)
expect(page).to have_selector('.switch-to-mobile-site-banner')
end
end
Since Kaleidoscope was the first to come up with a working set of specs that took me most of the way, I'm awarding him the bounty, but I'm going to accept Billy Chan's answer as correct because I think it is a more 'canonical' solution that I would direct others to reference in the future.
Upvotes: 4
Views: 1675
Reputation: 3627
Enable js in your spec and stick this in IMMEDIATELY before you request the page you're spec'ing (it only needs to go before the request you care about but just try this first):
page.driver.headers = { "User-Agent" => "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9A334 Safari/7534.48.3" }
UPDATE: here's the code for my working spec:
root markup:
<% for_device_type :mobile do %>
HAHA LOL MOBILE
<% end %>
spec:
require 'spec_helper'
feature 'Switch to mobile site banner' do
scenario 'desktop content', js: true do
visit "/"
expect(page).to_not have_content('LOL')
end
scenario 'mobile content', js: true do
page.driver.headers = { "User-Agent" => "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9A334 Safari/7534.48.3" }
visit "/"
expect(page).to have_content('LOL')
end
end
UPDATE 2 As for why your original spec isn't working, you aren't actually mocking the env afaik. I'd be surprised if feature specs let you access the env or request objects that easily. You should be working from the POV of the browser and so setting the user agent seems like the way to go.
Upvotes: 2
Reputation: 24815
At first, you need to turn on Javascript in this feature as mobile detection is done by Javascript in this gem, and many other similar solutions.
feature 'Switch to mobile site banner', js: true do
Update: According to Andrey's comment, Mobivious dosen't use JS to detect mobile so you don't need JS enabled.
Secondly, I suggest you not to use low level mock in the integration testing when possible. What you need to do is to fully behave like a mobile user and see what he will experience. You don't even need the Mobvious helpers which are low level as well.
I searched around and found this gem could be of help: https://github.com/mururu/capybara-user_agent
Though I havn't used that gem before, the syntax looks easy
feature 'Switch to mobile site banner', js: true do
background do
set_user_agent(:iphone)
end
scenario 'mobile user prefers using the desktop site' do
visit root_url(desktop: 1)
expect(page).to have_selector('.switch-to-mobile-site-banner')
end
end
Additional answer to OP's comment
About reaching 'example.com'. The fix is to use root_path
instead of root_url
in visit
line. This is a recommended practice to remove dependency on domain settings.
About user agent setting. I checked the gem, the syntax looks different from what Poltergeist can do. So, try not to use the gem but set the header as per Poltergeist's issue
page.driver.headers = {"User-Agent:" => "iphone"}
Reference: https://github.com/jonleighton/poltergeist/issues/127
If this still doesn't work, you can use Webkit temporally in this tests. After all they are all headless drivers. I always install these two gems and use Poltergeist as the main one as well. Just set it as below after gem installation.
In the test
require 'spec_helper'
require 'capybara-webkit'
feature 'Switch to mobile site banner', js: true do
capybara.javascript_driver = :webkit
Upvotes: 4