Is there a good way to check on existence of images and favicons using rspec and capybara?
I can check on the DOM of favicons and images, but I want to be able to check that those images load as well. Is this possible with rspec and capybara?
describe "check images and favicon" do
before { visit "url/to/check")
it "should have the images" do
page.should have_css('img', text: "image1.jpg")
it "should have the favicon" do
page.should have_xpath("/html/head/link[#href='favicon.ico']"
end
end
# frozen_string_literal: true
module Capybara
module CustomMatchers
include Capybara::DSL
class Asset
def asset_exists?(actual, src)
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
status == 200 || status == 302
end
end
class LoadImage < Asset
def initialize(*args)
#args = args
#src = args.first
end
def matches?(actual)
is_present = actual.has_selector?("img[src='#{#src}']")
is_present && asset_exists?(actual, #src)
end
def does_not_match?(actual)
actual.has_no_selector?("img[src='#{#src}']")
end
def failure_message
"No image loaded with source: '#{#src}'"
end
def failure_message_when_negated
"Image loaded with source: '#{#src}'"
end
def description
"Verify if image with source: '#{#src}' is loaded"
end
end
class LoadFavicon < Asset
def initialize(*args)
#args = args
#rel = args.first
#href = args.second
end
def matches?(actual)
is_present = actual.has_selector?("link[rel='#{#rel}'][href='#{#href}']", visible: false)
is_present && asset_exists?(actual, #href)
end
def does_not_match?(actual)
actual.has_no_selector?("link[rel='#{#rel}'][href='#{#href}']", visible: false)
end
def failure_message
"No favicon loaded with rel: '#{#rel}' and href: '#{#href}'"
end
def failure_message_when_negated
"Favicon loaded with rel: '#{#rel}' and href: '#{#href}'"
end
def description
"Verify if favicon with rel: '#{#rel}' and href: '#{#href}' is loaded"
end
end
def load_image(*args)
LoadImage.new(*args)
end
def load_favicon(*args)
LoadFavicon.new(*args)
end
end
end
RSpec.configure do |config|
config.include Capybara::CustomMatchers
end
Check https://gist.github.com/yovasx2/1c767114f2e003474a546c89ab4f90db to star and download
The question is to find out if the actual img and favicon are present. Here is the code to check that all images for the slider are present.
page.all('#carousel img').each do |img|
visit img[:src]
page.status_code.should be 200
end
For individual image with id myimage use
visit page.find('img#myimage')[:src]
page.status_code.should be 200
And for favicon simplest is to run the following
page.all('link[rel~="icon"]', visible: :any).each do |fav|
visit fav[:href]
page.status_code.should be 200
end
To test for all broken images on a page versus,
a specific image,
the presence of a selector, or
the image availability in the pipeline
I gather all the images and check a "get" response using the following:
describe "Check for Images" do
before { visit page_path }
it "should not have broken images" do
all_images = page.all('img')
all_images.each do |img|
get img[:src]
expect(response).to be_successful
end
end
end
Hope that helps.
Related
Hi I Try to create a mock for follow class:
module EstablishmentsQueryService
class << self
def find_by_id(id)
Establishment.find_by!(id:)
rescue ActiveRecord::RecordNotFound
raise EstablishmentNotFoundError.new id
end
end
end
to try test my controller
# frozen_string_literal: true
module Api
module V1
# Controllewr to manager Establishments
class EstablishmentsController < Api::V1::ApiController
before_action :validate_id, only: %i[destroy update show]
before_action :load_establishment, only: %i[destroy update show]
def show; end
def create
#establishment = Establishment.new(establishment_params)
#establishment = EstablishmentService.save(#establishment)
render status: :created
end
def destroy
EstablishmentService.delete(#establishment)
end
def update
#establishment.attributes = establishment_params
#establishment = EstablishmentService.save(#establishment)
end
private
def validate_id
message = I18n.t('establishment_controller.id.invalid', id: params[:id])
UuidValidateService.call(params[:id], message)
end
def load_establishment
#establishment = EstablishmentsQueryService.find_by_id(params[:id])
end
def establishment_params
params.require(:establishment).permit(:name, :cnpj, :description)
end
end
end
end
follow my test:
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Api::V1::Establishments', type: :request do
describe 'GET /api/v1/establishments/:id' do
context 'when has establishment' do
let(:establishment) { build(:establishment, id: p, created_at: DateTime.now, updated_at: DateTime.now) }
before do
allow_any_instance_of(EstablishmentsQueryService).to receive(:find_by_id).and_return(establishment)
get "/api/v1/establishments/#{establishment.id}"
end
it 'then http status is ok' do
expect_status_is_ok
end
it 'has body equal to expected' do
except_field_by_field(establishment, body_to_open_struct, %i[id name cnpj description])
end
end
context 'when has no establishment' do
before do
get "/api/v1/establishments/#{UUID.new.generate}"
end
it 'then http status is not_found' do
expect_status_is_not_found
end
end
context 'when use invalid id' do
before { get "/api/v1/establishments/#{FFaker::Lorem.word}" }
it 'then http status is bad_request' do
expect_status_is_bad_request
end
end
end
describe 'PUT /api/v1/establishments/:id' do
let(:establishments_query_service) { allow(EstablishmentsQueryService) }
let(:establishments_service) { allow(EstablishmentsService) }
context 'when updated with success' do
let(:establishment) { build(:establishment) }
let(:id) { UUID.new.generate }
before do
establishments_query_service.to receive(:find_by_id) { |p| build(:establishment, id: p, created_at: DateTime.now, updated_at: DateTime.now) }
establishments_service.to receive(:save) do |p|
to_return = p
to_return.created_at = DateTime.now
to_return.updated_at = DateTime.now
end
put "/api/v1/establishments/#{id}"
end
it 'then http status is ok' do
expect_status_is_ok
end
it 'has body equal to expected' do
actual = body_to_open_struct
except_field_by_field(establishment, actual, %i[name cnpj description])
expected(actual.id).to eq(id)
end
end
context 'when has no establishment' do
end
context 'when has constraint violation' do
end
end
describe 'DELETE /api/v1/establishments/:id' do
end
describe 'POST /api/v1/establishments' do
end
end
If I work using allow_any_instance_of a test ignore configuration, use a real configuration and fails because has no data stores. If I use double I received a follow error:
Api::V1::Establishments GET /api/v1/establishments/:id when has establishment then http status is ok
Failure/Error: allow_any_instance_of(EstablishmentsQueryService).to receive(:find_by_id).and_return(establishment)
EstablishmentsQueryService does not implement #find_by_id
I think the right away is user allow_any_instance_of because this config is for static methods, but didn't work
how can I mock my class to test my controller? I using Ruby 3.1.2, rails 7.0.3 and rspec-rails 5.1.2
thank you
I found my problem, I forgot to definie expected params in my confi using with()
allow(EstablishmentsQueryService).to receive(:find_by_id).with(establishment.id).and_return(establishment)
I am having difficulties with testing image download.
def download_img
#image = Photo.find params[:id] unless params[:id].nil?
#c = Cat.find params[:cat_id] unless params[:cat_id].nil?
#foo = #image.foo unless #image.nil?
send_file(Paperclip.io_adapters.for(#image.file).path, type: "jpeg", :disposition => "attachment", :filename => #image.name)
end
My RSpec test (controller):
describe 'download_img' do
before do
get :download_img, { id: image.id }
end
it 'retrieves image by id' do
img = Photo.find image.id
expect(img).not_to be_nil
end
it 'downloads image' do
page.response_headers["Content-Type"].should == "application/jpg"
page.response_headers["Content-Disposition"].should == "attachment; filename=\"image.name.jpg\""
end
end
When I run rspec test for both tests I get an error: "No such file or directory # rb_sysopen - /home/public/system/photos/files/000/000/001/foo/IMG123.JPG"
Thank you.
Is that image you are referring to in Photo.find image.id coming from fixtures? Will you please check if there's a corresponding file referenced from fixtures. I suppose you'll have to change the path in fixtures and create that file as well, a good place for it is spec/fixtures.
FactoryGirl.define do
factory :card do
card_no '6217920016000864'
factory :invalid_card do
card_no nil
end
end
end
card_controller_spec.rb
describe CardsController do
describe 'GET #index' do
it 'assigns card' do
card = create(:card)
get :index
expect(assigns(:card)).to eq([card])
end
it 'show index' do
expect(response).to render_template("index")
end
end
end
cards_controller.rb
class CardsController < ApplicationController
def index
if current_user.login_name=="admin"
#admin
#cardlist = set_paginate Card
else
#普通管理员
#restaurant_ids = Restaurant.where('parent_id = ? or id = ?', current_user.restaurant.id, current_user.restaurant.id).collect { |r| r.id }
#cardlist = set_paginate Card.where('restaurant_id in (?) ', #restaurant_ids)
end
end
end
two errors like this expecting <"index"> but rendering with <""> and expect(assigns(:card)).to eq([card]) got nil.
help me thanks!
First test is for what data controller provides to the view. Looking on the controller you have you should check for assigns(:cartdlist) I think. And use match_array rather than eq.
Second test doesn't render anything as it doesn't go anywhere (your get is just for the first it block). So try this:
describe CardsController do
describe 'GET #index' do
before do
card = create(:card)
get :index
end
it 'assigns card' do
expect(assigns(:cardlist)).to match_array([card])
end
it 'show index' do
expect(response).to render_template("index")
end
end
end
It's probably not the problem in your case as you got different error but to allow expect syntax your spec_helper.rb file should contain:
RSpec.configure do |config|
# most omitted ...
config.expect_with :rspec do |c|
c.syntax = :expect
end
end
or
c.syntax = [:should, :expect]
for both
This
expect(assigns(:card)).to eq([card]) got nil
because I believe you assign data to cards (after updating question: cardlist)
This
expecting <"index"> but rendering with <"">
because you don't call get :index in test
I'm implementing a lazy login feature. My cucumber feature should describe it:
Feature: User log in
Scenario: Lazy login
Given I didn't log out the last time I was on the site
When I go to the homepage
Then I should automatically be logged in
And these are my step definitions:
Given(/^I didn't log out the last time I was on the site$/) do
user = FactoryGirl.create(:user)
visit new_user_session_path
fill_in('user[email]', with: user.email)
fill_in('user[password]', with: 'test123')
click_button('Sign in')
Capybara.reset_sessions!
end
When(/^I go to the homepage$/) do
visit root_path
end
Then(/^I should automatically be logged in$/) do #<-- Fails here
page.should have_content("Logout")
end
This is what happens when a user logs in: the cookies.signed[:auth_token] gets set. This will be used by a before filter in my ApplicationController so that users who open a fresh browser will be logged in automatically:
class SessionsController < Devise::SessionsController
def create
super
if user_signed_in?
puts 'yesssssss'
session[:user_id] = current_user.id
current_user.remember_me! if current_user.remember_token.blank?
cookies.signed[:auth_token] = {
:value => current_user.remember_token,
:domain => "mysite.com",
:secure => !(Rails.env.test? || Rails.env.development?)
}
puts "current_user.remember_token = #{current_user.remember_token}"
puts 'cookies:'
puts cookies.signed[:auth_token]
end
end
end
This is the before filter in my ApplicationController:
def sign_in_through_cookie
logger.info "logging in by cookie"
puts "logging in by cookie"
puts cookies.signed[:auth_token] #<-- PROBLEM: this returns nil.
return true if !current_user.nil?
if !cookies[:auth_token].nil? && cookies[:auth_token] != ''
user = User.find_by_remember_token(cookies.signed[:auth_token])
return false if user.blank?
sign_in(user)
puts 'success'
return true
else
return false
end
end
So the issue is that in the last step of my cucumber feature, cookies.signed[:auth_token] returns nil. I'm guessing this is just a capybara thing. So do I actually have to set a cookie in the test as opposed to using the one in my controller?
So eventually I figured it out after trying a lot of different things.
Given(/^I didn't log out the last time I was on the site$/) do
user = FactoryGirl.create(:user)
visit new_user_session_path
fill_in('user[email]', with: user.email)
fill_in('user[password]', with: 'test123')
click_button('Sign in')
Capybara.current_session.driver.request.cookies.[]('auth_token').should_not be_nil
auth_token_value = Capybara.current_session.driver.request.cookies.[]('auth_token')
Capybara.reset_sessions!
page.driver.browser.set_cookie("auth_token=#{auth_token_value}")
end
When(/^I go to the homepage$/) do
visit root_path
end
Then(/^I should automatically be logged in$/) do
page.should have_content("Logout")
end
UPDATE:
Here's what I use in case I'm using Selenium for some of the tests:
if Capybara.current_session.driver.class == Capybara::Selenium::Driver
auth_token = page.driver.browser.manage.cookie_named('auth_token')[:value]
page.driver.browser.manage.delete_all_cookies
page.driver.browser.manage.add_cookie(:name => "auth_token", :value => auth_token)
else
puts "cookies = #{Capybara.current_session.driver.request.cookies}"
Capybara.current_session.driver.request.cookies.[]('auth_token').should_not be_nil
auth_token_value = Capybara.current_session.driver.request.cookies.[]('auth_token')
Capybara.reset_sessions!
page.driver.browser.set_cookie("auth_token=#{auth_token_value}")
end
Use https://github.com/nruth/show_me_the_cookies which wraps the driver methods. It has methods for getting cookies, deleting cookies, and a method for creating cookies called create_cookie.
I needed just to test the cookie values
Inspiration taken from https://collectiveidea.com/blog/archives/2012/01/05/capybara-cucumber-and-how-the-cookie-crumbles
and ported to Rails 5.x
Create features/support/cookies.rb
With content
module Capybara
class Session
def cookies
#cookies ||= ActionDispatch::Request.new(Rails.application.env_config.deep_dup).cookie_jar
end
end
end
Before do
allow_any_instance_of(ActionDispatch::Request).to receive(:cookie_jar).and_return(page.cookies)
allow_any_instance_of(ActionDispatch::Request).to receive(:cookies).and_return(page.cookies)
end
Then the step for testing
Then('is set cookie {string} with value {string}') do |cookie, value|
expect(page.cookies.signed[cookie]).to eq value
end
I'm writing specs for a plugin which has different modules that the user can choose to load.
Some of these modules dynamically add before_filters to ApplicationController.
The problem is sometimes if the spec for module X runs and adds a before_filter, the spec for module Y which runs later will fail. I need somehow to run the second spec on a clean ApplicationController.
Is there a way to remove before filters or reload ApplicationController completely between specs?
For example in the following specs, the second 'it' does not pass:
describe ApplicationController do
context "with bf" do
before(:all) do
ApplicationController.class_eval do
before_filter :bf
def bf
#text = "hi"
end
def index
#text ||= ""
#text += " world!"
render :text => #text
end
end
end
it "should do" do
get :index
response.body.should == "hi world!"
end
end
context "without bf" do
it "should do" do
get :index
response.body.should == " world!"
end
end
end
You should be able to do this using context blocks to separate the two sets of examples.
describe Something do
context "with module X" do
before(:each) do
use_before_fitler
end
it_does_something
it_does_something_else
end
context "without module X" do
it_does_this
it_does_that
end
end
The before_filter should only affect the examples in the "with module X" context.
I'd use separate specs on subclasses rather than ApplicationController itself:
# spec_helper.rb
def setup_index_action
ApplicationController.class_eval do
def index
#text ||= ""
#text += " world!"
render :text => #text
end
end
end
def setup_before_filter
ApplicationController.class_eval do
before_filter :bf
def bf
#text = "hi"
end
end
end
# spec/controllers/foo_controller_spec.rb
require 'spec_helper'
describe FooController do
context "with bf" do
before(:all) do
setup_index_action
setup_before_filter
end
it "should do" do
get :index
response.body.should == "hi world!"
end
end
end
# spec/controllers/bar_controller_spec.rb
require 'spec_helper'
describe BarController do
before(:all) do
setup_index_action
end
context "without bf" do
it "should do" do
get :index
response.body.should == " world!"
end
end
end