after_request in capybara (in rspec)? - ruby-on-rails

We want to validate after every request that there is no escaped HTML or XSS on the page. In Cucumber, we have an AfterStep that does this (as long as the current page is not the same as the previous page).
Is there any way to do this?
EDIT: See https://gist.github.com/603295 for an example of an old (no longer working) version of what I'm hoping to find.

Rspec gives you these after hooks:
https://www.relishapp.com/rspec/rspec-core/v/2-0/docs/hooks/before-and-after-hooks
In short:
after(:each)
after(:all)
after(:suite)

This is the best I could come up with, which was pretty close (however, it's still only after every example, not after every request):
In my spec helper, I added something like this:
RSpec.configure do |config|
config.after(:each, type: :feature) do
# this is run after each example in Capybara, and I did stuff like
page.body.should_not match(UNESCAPED_REGEX)
end
end

Related

Build up help for TDD/BDD with RSpec

For about 2 weeks ago i've started learning Ruby, i've made an WebParser / Page WordCounter using 'open-uri' and 'nokogiri'. So i just run up the terminal with 'ruby counter.rb http://test.com word' and i and up with the number of matches of that word, case insensitive so i can grab everything.
SO here I am learning about RSpec, TDD, BDD and all this stuff, and i would like to know how my code could be constructed using RSpec examples and expectations. Ive already read all the documentation, i'm building simples examples to test, etc.
I would like to know if there is anyone that could build my code into RSpec examples and expectations, so I can study what you've done and how you've done.
Here is my code:
require 'open-uri'
require 'nokogiri'
class Counter
def initialize(url)
#url = url
end
def count(word, url)
doc = Nokogiri::HTML(open(url))
doc.css('head').remove
doc.text.scan(/#{word}/i).size
end
end
url, word = ARGV
puts "Found: #{Counter.new(url).count(word, url)} matches."
Hope someone could help me, i'm really into ruby and found this RSpec amazing,
Thanks guys, i'll be studying and waiting!
there's an rspec --init command that will create your boilerplate.
Once you've done this, open spec_helper.rb and require your code file.
By the way, it's a little odd that your initialize accepts a url and assigns it to an instance variable, but the count method takes a url as an argument.
So assuming it's refactored to this:
attr_reader :url
def initialize(url)
#url = url
end
def count(word)
doc = Nokogiri::HTML(open(url)) # this uses the attr_reader
doc.css('head').remove
doc.text.scan(/#{word}/i).size
end
Then you can write a test case like this (this is not a comprehensive coverage, just an example):
describe "Counter" do
let(:url) { "http://some_url" }
let(:counter) { Counter.new url }
it "counts words" do
expect(counter.count("foo")).to(
eq("<whatever the expected result is>")
)
end
end
Using let to set variables is optional. You could also set the variables inside the it ... do block, but you'd have to repeat it for each case.
In addition to .to you have .not_to, and there are many other helpful methods than eq. I recommend reading the RSpec matcher docs to familiarize yourself with these.
It's also worth mentioning that this test case will make an HTTP call which is sometimes desired, and sometimes not. For example if you have many cases and want to run them quickly, then removing HTTP calls will be beneficial. But doing this means that you are no longer actually testing the state of the url. What if the markup changes? Unless your test case actually makes the HTTP call, you won't know for sure.
Still, it's good to know how to remove the HTTP call since the underlying concept ("mocking" or "stubbing") has a lot of uses. Something like this:
it "counts words" do
mock_html = <<-HTML
<!doctype html>
<html lang='en'>
<head></head>
<body>foo</body>
</html>
HTML
expect(Object).to(
receive(:open).with(any_args).at_least(1).times.and_return(mock_html)
)
expect(counter.count("foo")).to eq(1)
expect(counter.count("bar")).to eq(0)
end
any_args is a special term you can use when stubbing methods. You could also use url here since you know what the passed argument is going to be.
For more info, again I'll refer you to RSpec's docs, this time the ones about mocking / stubbing.
Generally you want to focus mostly on input/output of functions. Sometimes you will want to check that another method is called (you'd use mock/stub for this) but it's probably not expected of you to test every single line of code.

Capybara click on link which executes js doesn't working

I have this code while using Capybara:
a = page.find(".show-link")
a.click
expect(page).not_to have_css(".someClass.hidden")
And I have link
<a class="show-link" href="">Show me!</a>
I have also js function bind to click event which removes hidden class from element:
$("body").on("click", ".show-link",function(){$('.someClass').removeClass('hidden');return false;});
But instead of executing my js function on click I get redirected to another page.
I tried to set href = "#", I tried to change <a> to <span> but doesn't work
script is not executed.
In your test definition, add :js => true, so:
describe 'some stuff which requires js', :js => true do
it 'will test this or that' do
...
end
end
Capybara will then use Selenium (default driver) to test your javascript functionality. It's quite cool as it uses an actual browser window.
Christian-G is correct if you are using rspec. If in case you are using Minitest the same idea applies but it would be executed something like:
test "this is my test" do
Capybara.current_driver = Capybara.javascript_driver
#test logic goes here
Capybara.use_default_driver
end
Ideally you would not change the capybara driver inline like this, but put them in your setup and teardown calls for the test. like this:
setup do
Capybara.current_driver = Capybara.javascript_driver
end
teardown do
Capybara.use_default_driver
end
This is also outlined in the Capybara documentation.
As it was already mentioned, you need to use some driver that supports js execution (webkit, poltergeist, selenium).
Also there should be only one matching element, i.e. if there's one element ".someClass.hidden" and another one ".someClass.shown" test won't pass
An one more option is that you have wrong usage of Capybara matcher:
expect(page).not_to have_css(".someClass.hidden")
should be
expect(page).to have_no_css(".someClass.hidden")
Why?
Capybara has logic to wait for something to happen, so in this case it checks that page has .someClass.hidden and immediately finds such element. If it were have_no_css it will find element, but then retry several times waiting for it to disappear

Is it possible to call GET in a Capybara/Rspec integration test?

I have a Rails 4.2 application....I was adding content compression via this thoughtbot blog post, but I get an error such as:
undefined method `get' for #<RSpec::ExampleGroups::Compression:0x00000009aa4cc8>
Perusing over the capybara docs, it seems like you shouldn't be using get. Any idea how to test the below then in Rails 4?
# spec/integration/compression_spec.rb
require 'spec_helper'
feature 'Compression' do
scenario "a visitor has a browser that supports compression" do
['deflate','gzip', 'deflate,gzip','gzip,deflate'].each do|compression_method|
get root_path, {}, {'HTTP_ACCEPT_ENCODING' => compression_method }
response.headers['Content-Encoding'].should be
end
end
scenario "a visitor's browser does not support compression" do
get root_path
response.headers['Content-Encoding'].should_not be
end
end
In a capybara test you would use visit not get (as described here), but that answer won't actually help you because the test you've written above is not an integration test, it's a controller test.
Move it to spec/controllers and use the controller-specific helpers describe/context/it etc. to construct your tests for your controller. You can set the headers and do the sorts of checks that you're doing in the code you're showing.

RSpec - programatically determing type of spec

I was wondering whether it is possible to find out at runtime (i.e. in spec_helper.rb) what kind of spec is being executed (i.e. request, functional etc)?
Many Thanks!
If you're looking to add something like a before filter for only a certain type of spec, consider writing:
RSpec.configure do |config|
config.before(:each, type: :request) do
# request-spec only before hook
end
end
If you're trying to do something more complicated, you can access the current example's type via example.metadata[:type].
The way the rspec/rails project makes this distinction is by checking the file path.
For instance, a request spec will have a file path that matches /spec\/request/.

Use request helpers outside of controller specs

I'd like to include the request helpers (from ActionDispatch::Integration::RequestHelpers [ApiDock], like post and xhr methods) also in some specs outside of my controller specs. The problem is that these request helpers are only included in spec/controller and when a controller is described.
What do I have to include/require in those specs?
I am using RSpec 2 and Rails 3.
I just solved the problem by including the below code in my acceptance helper. If you are not Steak then just simply put it in spec helper or require it from somewhere else. post and xhr methods are now available in that spec regardless in what spec it is or in what directory you are.
The code is derived from RSpec::Rails::RequestExampleGroup
RSpec::Core::ExampleGroup.class_eval do
include ActiveSupport::Concern
include ActionDispatch::Integration::Runner
include RSpec::Rails::BrowserSimulators
def app
::Rails.application
end
def last_response
response
end
end
I know it's 4 years later and many things have of course changed, but since I stumbled on this question while searching how to make other tests behave like controller tests (and thus have post and get methods and the like) I wanted to point out this solution that works with RSpec 3: if you add this to the spec_helper
config.include RSpec::Rails::RequestExampleGroup, type: :request, example_group: { file_path: /spec\/(api|integration)/
it will make all tests in the given path have support for controller methods.

Resources