How to test the response code with Capybara + Selenium - ruby-on-rails

I have the following spec:
it "deletes post", :js => true do
...
...
page.status_code.should = '404'
end
The page.status_code line is giving me this error:
Capybara::NotSupportedByDriverError
How do I check the page's status code?

As an aside. This line
page.status_code.should = '404'
Should be
page.status_code.should == 404
This worked for me with capybara-webkit.

status_code is not currently supported by the Selenium driver. You will need to write a different test to check the response status code.

Either switch to another driver (like rack-test) for that test, or test that the displayed page is the 404 page (should have content 'Not Found' in h1).
As #eugen said, Selenium doesn't support status codes.

Selenium web driver doest not implement status_code and there is no direct way to test response_code with selenium (developer's choice).
To test it I added in my layout/application.html.erb:
<html code="<%= response.try(:code) if defined?(response) %>">[...]</html>
And then in my test:
def no_error?
response_code = page.first('html')[:code]
assert (response_code == '200'), "Response code should be 200 : got #{response_code}"
end

Try it out
expect(page).to have_http_status(200)

Use js to make a request and get status as below:
js_script = <<JSS
xhr = new XMLHttpRequest();
xhr.open('GET', '#{src}', true);
xhr.send();
JSS
actual.execute_script(js_script)
status = actual.evaluate_script('xhr.status') # get js variable value
Check https://gist.github.com/yovasx2/1c767114f2e003474a546c89ab4f90db for more details

Related

RSpec failing because URL is being escaped

Moving RSpec in a Rails project from using Poltergeist to using Selenium (Webdriver) Chrome and now I'm getting some failures I wasn't quite expecting. Looks like it's trying to escape the URL for some reason? Thoughts?
Failures:
1) Story editing private story should be read after editing
Failure/Error: expect(story_cover_image).to eq "#{url}/convert?rotate=exif"
expected: "http://placehold.it/edited.png/convert?rotate=exif"
got: "\"http://placehold.it/edited.png/convert?rotate=exif\""
(compared using ==)
Here's the spec (removed what's not relevant):
feature 'Story editing', type: :feature, js: true do
...
let(:story_attributes) do
{
...
cover_image: {
url: 'http://placehold.it/edited.png'
}
}
end
...
def fill_file_url(url)
execute_script <<-JS
angular.element(".content").scope().story.addCoverImage();
angular.element(".content").scope().story.coverImage.url = "#{url}";
angular.element(".content").scope().story.save();
JS
expect(story_cover_image).to eq "#{url}/convert?rotate=exif"
end
...
def fill_in_story_form(story)
fill_file_url story[:cover_image][:url] if story[:cover_image]
end
...
context 'private' do
scenario 'story should be read after editing' do
fill_in_story_form story_attributes <<<<<< SPEC FAILS ON THIS LINE >>>>>
...
end
end
EDIT New failure after changing "#{url}" to #{url} as per request:
Also tried: angular.element(".content").scope().story.coverImage.url = url;
1) Story editing private story should be read after editing
Failure/Error:
execute_script <<-JS
angular.element(".content").scope().story.addCoverImage();
angular.element(".content").scope().story.coverImage.url = #{url};
angular.element(".content").scope().story.save();
JS
Selenium::WebDriver::Error::UnknownError:
unknown error: Runtime.evaluate threw exception: SyntaxError: Unexpected token :
(Session info: chrome=69.0.3497.100)
(Driver info: chromedriver=2.41.578706 (5f725d1b4f0a4acbf5259df887244095596231db),platform=Mac OS X 10.13.6 x86_64)
The problem is in this line:
angular.element(".content").scope().story.coverImage.url = "#{url}";
Because inside a HEREDOC everything is considered to be a string #{url} is all you need to do string interpolation. But in this case you want the interpolation to be treated as a Javascript string so you should use '#{url}'. Doing "#{url}" creates escaped double-quotes because by default a HEREDOC follows double-quoting rules, so you should change all your double quotes to single quotes inside the HEREDOC.
Trying it directly into the irb console you get:
url = "https://www.google.com"
<<-JS
angular.element('.content').scope().story.coverImage.url = '#{url}';
JS
=> "angular.element('.content').scope().story.coverImage.url = 'https://www.google.com';\n";
<<-JS
angular.element(".content").scope().story.coverImage.url = '#{url}';
JS
=> "angular.element(\".content\").scope().story.coverImage.url = 'http://www.google.com';\n"
Quoting rules of Heredoc docs

Weird error when testing a Rails + AngularJS app with Rspec and PhantomJS

I have an app that lists tickets. It uses AngularJS. Here's the controller action:
def index
#tickets = apply_scopes(#tickets)
response.headers['x-total-count'] = #tickets.total_count
response.headers['x-per-page'] = Ticket.default_per_page
end
The Angular controller (Coffeescript):
$scope.fetch = ->
Ticket.query($scope.search).then (response) ->
$scope.tickets = response.data
$scope.totalCount = response.headers('x-total-count')
$scope.perPage = response.headers('x-per-page')
$scope.fetch()
I'm using angular-rails-resource to fetch the records. Everything works smoothly if I test by hand.
Here is the spec:
let(:user) { create :user }
scenario 'User lists tickets', js: true do
login_as user, scope: :user
ticket = create :ticket, user: user
visit root_path
click_on 'Support Requests'
expect(page).to have_content(ticket.subject)
end
When I run this spec, I just get the regular Rspec failure message because the condition was not met, but it should have:
expected to find text "ticket 000" in...
I figured it had something to do with concurrency and Capybara not waiting for Angular to fetch and display the records. Then I went ahead and added a sleep 2 right above the expectation just to test that. When I do it, I get a different error:
Capybara::Poltergeist::JavascriptError:
One or more errors were raised in the Javascript code on the page. If you don't care about these errors, you can ignore them by setting js_errors: false in your Poltergeist configuration (see documentation for details).
Possibly unhandled rejection: {"data":"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">\n<HTML>\n <HEAD><TITLE>Internal Server Error</TITLE></HEAD>\n <BODY>\n <H1>Internal Server Error</H1>\n undefined method `split' for 1:Fixnum\n <HR>\n <ADDRESS>\n WEBrick/1.3.1 (Ruby/2.3.3/2016-11-21) at\n 127.0.0.1:54674\n </ADDRESS>\n </BODY>\n</HTML>\n","status":500,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","url":"/tickets","params":{},"headers":{"Accept":"application/json","Content-Type":"application/json"},"data":{}},"statusText":"Internal Server Error "}
Possibly unhandled rejection: {"data":"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">\n<HTML>\n <HEAD><TITLE>Internal Server Error</TITLE></HEAD>\n <BODY>\n <H1>Internal Server Error</H1>\n undefined method `split' for 1:Fixnum\n <HR>\n <ADDRESS>\n WEBrick/1.3.1 (Ruby/2.3.3/2016-11-21) at\n 127.0.0.1:54674\n </ADDRESS>\n </BODY>\n</HTML>\n","status":500,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","url":"/tickets","params":{},"headers":{"Accept":"application/json","Content-Type":"application/json"},"data":{}},"statusText":"Internal Server Error "}
at http://127.0.0.1:54674/assets/application-713835b1641be632b29f7502c00a879e171bca5d6d06a5f264afcd819c123e76.js:14363
Here is my stack:
rails (5.0.2)
capybara (2.12.1)
poltergeist (1.13.0)
rspec-core (3.5.4)
phantomjs 2.1.1
Additional info:
If I output something right before ending the controller action, it gets outputted. The execution is going through the entire action;
If I console.log something right before fetching tickets, it's outputted as well. However, the Promise is not being resolved.
I found out the issue was with my pagination headers (x-total-count and x-per-page). Converting them to String does the trick. The weird part is that it was working OK in development, but not in test environment. So, if anyone has this issue in the future, the solution in my case was:
def index
#tickets = apply_scopes(#tickets)
response.headers['x-total-count'] = #tickets.total_count.to_s
response.headers['x-per-page'] = Ticket.default_per_page.to_s
end
Notice .to_s being called when assigning the headers.

rails wisper under test

I have a project which used wisper https://github.com/krisleech/wisper to provide publisher and subscribers functionalities.
The gem works perfectly under development and production modes. However, when I try to add some tests for them (rake test:integration), the newly added tests refused to work. The publisher (maybe also the listener) in the tests mode stopped working anymore.
Core::Request.subscribe(Listener::Studentlistener, async: true)
Core::Request.subscribe(Listener::Tutorlistener, async: true)
I used the sidekiq as a async backend, i used wisper-sidekiq gem to handle the async requests, not sure if this would be the problem?
,puma as the server, MRI ruby 2.0.0
Do I have to a set up something in order for the test to run?
it "Student can get latest status after looking for xxx tutor" do
post api_v1_students_request_look_for_xxx_tutor_path,
{ subject: 'nothing' },
{ "AUTHORIZATION" => "xxx"}
expect(response).to be_success
get api_v1_students_status_path, nil,
{ "AUTHORIZATION" => "xxx"}
expect(response).to be_success
json_response = JSON.parse(response.body)
expect(json_response['state']).to eq('matching')
end
The listener should receive the publishing between these two posts and update the state to be "matching". However, now when I run rspec the test failed because the publisher never publish anything and hence the state is not updated correctly.
Even the authors are relying on some mocking/stubbing in the integrations tests, so that might be the correct way.
class MyCommand
include Wisper::Publisher
def execute(be_successful)
if be_successful
broadcast('success', 'hello')
else
broadcast('failure', 'world')
end
end
end
describe Wisper do
it 'subscribes object to all published events' do
listener = double('listener')
expect(listener).to receive(:success).with('hello')
command = MyCommand.new
command.subscribe(listener)
command.execute(true)
end
https://github.com/krisleech/wisper/blob/master/spec/lib/integration_spec.rb

Stubbing out Doorkeep Token using rspec_api_documentation

I'm building an API in Rails 4 using rspec_api_documentation and have been really impressed. Having opted to use DoorKeeper to secure my endpoints, I'm successfully able to test this all from the console, and got it working.
Where I am having difficulty now is how to spec it out, and stub the token.
DoorKeeper's documentation suggests using the following:
describe Api::V1::ProfilesController do
describe 'GET #index' do
let(:token) { stub :accessible? => true }
before do
controller.stub(:doorkeeper_token) { token }
end
it 'responds with 200' do
get :index, :format => :json
response.status.should eq(200)
end
end
end
However, I've written an acceptance test in line with rspec_api_documentation. This is the projects_spec.rb that I've written:
require 'spec_helper'
require 'rspec_api_documentation/dsl'
resource "Projects" do
header "Accept", "application/json"
header "Content-Type", "application/json"
let(:token) { stub :accessible? => true }
before do
controller.stub(:doorkeeper_token) { token }
end
get "/api/v1/group_runs" do
parameter :page, "Current page of projects"
example_request "Getting a list of projects" do
status.should == 200
end
end
end
When I run the test I get the following:
undefined local variable or method `controller' for #<RSpec::Core
I suspect this is because it's not explicitly a controller spec, but as I said, I'd rather stick to this rspec_api_documentation way of testing my API.
Surely someone has had to do this? Is there another way I could be stubbing the token?
Thanks in advance.
I had the same problem and I created manually the access token with a specified token. By doing that, I was then able to use my defined token in the Authorization header :
resource "Projects" do
let(:oauth_app) {
Doorkeeper::Application.create!(
name: "My Application",
redirect_uri: "urn:ietf:wg:oauth:2.0:oob"
)
}
let(:access_token) { Doorkeeper::AccessToken.create!(application: oauth_app) }
let(:authorization) { "Bearer #{access_token.token}" }
header 'Authorization', :authorization
get "/api/v1/group_runs" do
example_request "Getting a list of projects" do
status.should == 200
end
end
end
I wouldn't recommend stubbing out DoorKeeper in an rspec_api_documentation acceptance test. One of the benefits of RAD is seeing all of the headers in the examples that it generates. If you're stubbing out OAuth2, then people reading the documentation won't see any of the OAuth2 headers while they're trying to make a client.
I'm also not sure it's possible to do this nicely. RAD is very similar to a Capybara feature test and a quick search makes it seem difficult to do.
RAD has an OAuth2MacClient which you can possibly use, here.
require 'spec_helper'
resource "Projects" do
let(:client) { RspecApiDocumentation::OAuth2MACClient.new(self) }
get "/api/v1/group_runs" do
example_request "Getting a list of projects" do
status.should == 200
end
end
end

Why "assert_response" ignores the custom message in Rails?

In my functional test I verify the response status like that:
assert_response :error, "My error message"
However, Rails ignores my custom message and reports:
Expected response to be a <:error>, but was <201>
Any ideas why?
I use Rails 3.2.6.
I was wondering the same thing. Turns out it's been fixed on Github: https://github.com/rails/rails/commit/d28a15ede59f6434f1b7a8d01be060fa73b4746c
For me, updating rails/the actionpack gem to the latest version hadn't included that fix, but I was able to update response.rb manually. It was located at:
/Users/alex/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.8/lib/action_dispatch/testing/assertions/response.rb
In your test you have mentioned the response type as :error which matches to status code 500-599. But as your test is returning status code 201 it is not able to match that and showing the message according to the definition of assert_response which is
def assert_response(type, message = nil)
clean_backtrace do
if [ :success, :missing, :redirect, :error ].include?(type) && #response.send("#{type}?")
assert_block("") { true } # to count the assertion
elsif type.is_a?(Fixnum) && #response.response_code == type
assert_block("") { true } # to count the assertion
else
assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, #response.response_code)) { false }
end
end
end
it goes into else due to the type and status code mismatch and gives that message.
Here are the type of response and their matching codes:
:success: Status code was 200
:redirect: Status code was in the 300-399 range
:missing: Status code was 404
:error: Status code was in the 500-599 range

Resources