Capybara-webkit: disable iframe load during tests - ruby-on-rails

I'm testing a page that have various embedded youtube videos. Capybara-webkit is extremely slow to load and test that page, so I need to disable iframes load for that page.
The iframe looks like that:
<iframe class="embedly-embed" src="//cdn.embedly.com/widgets/media.html?src=http%3A%2F%2Fwww.youtube.com%2Fembed%2FKxu2A7-uBus%3Ffeature%3Doembed&url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DKxu2A7-uBus&image=http%3A%2F%2Fi.ytimg.com%2Fvi%2FKxu2A7-uBus%2Fhqdefault.jpg&key=7c4c130437b3403ab254d76c5015b5a5&type=text%2Fhtml&schema=youtube" width="854" height="480" scrolling="no" frameborder="0" allowfullscreen=""></iframe>
I tried two approaches:
Blacklisting the url:
Capybara.register_driver :webkit do |app|
# Disabling useless resources loading from capybara
driver = Capybara::Webkit::Driver.new(app)
driver.browser.url_blacklist = [
'//cdn.embedly.com/*',
'//cdn.embedly.com/widgets/*',
'http://cdn.embedly.com/widgets/*'
]
driver
end
Set all iframes as display: none;
(In application.html.erb)
<% if Rails.env.test? %>
<style>iframe {display:none}</style>
<% end %>
Both of them failed...
Any ideas?

Related

Rails/Capybara: How to make attach_file work with active storage direct upload?

I am having trouble to attach files to inputs which use direct upload inside my system tests (Capybara). All tests worked before I switched to direct upload. I have also tried to manually submit appropriate forms via Browser and everything works there. Unfortunately, no luck with Capybara :/.
Inside view, I have following input:
<%= f.input :desktop_files, as: :file, input_html: { direct_upload: true, multiple: true } %>
and file is attached to input in system test by:
attach_file 'uploads_create_assets_former[desktop_files][]', "#{fixture_path}/files/image.jpg"
When I try to run test which uses similar piece of code, I get:
Selenium::WebDriver::Error::UnexpectedAlertOpenError: unexpected alert open: {Alert text : Error reading image.jpg}
(Session info: headless chrome=94.0.4606.81)
and when I check console inside browser opened by Capabyra, I can see following error:
FileReader error
My suspicion is that Capabyra/Selenium has problem to access attached file, but I don't know about any other way how to assign file to input. Maybe there is some Capybara magic which comes to play here :) -- hopefully, I am not only one who uses Rails direct upload and needs to test this piece of code with system tests...
I am using:
ruby (3.0.0)
rails (6.1.4.1)
selenium-webdriver (4.0.3)
capybara (3.35.3)
webdrivers (4.7.0)
and for capybara:
Capybara.register_driver :headless_chrome do |app|
options = Selenium::WebDriver::Chrome::Options.new(
args: %w[headless disable-gpu no-sandbox window-size=1440x768]
)
options.add_preference(:download, prompt_for_download: false,
default_directory: Rails.root.join('tmp/downloads').to_s)
options.add_preference(:browser, set_download_behavior: { behavior: 'allow' })
Capybara::Selenium::Driver.new(app, browser: :chrome, capabilities: options)
end
Edit:
Html code of form which should do upload looks like this:
<form class="formtastic uploads_create_assets_former" id="new_uploads_create_assets_former" enctype="multipart/form-data" action="/admin/upload/create" accept-charset="UTF-8" method="post">
<fieldset class="inputs">
<ol>
<li class="file input optional" id="uploads_create_assets_former_desktop_files_input"><label for="uploads_create_assets_former_desktop_files" class="label">Dateien (Computer)</label>
<input id="uploads_create_assets_former_desktop_files" multiple="multiple" data-direct-upload-url="http://127.0.0.1:49538/rails/active_storage/direct_uploads" type="file" name="uploads_create_assets_former[desktop_files][]" />
</li>
</ol>
</fieldset>
<fieldset class="actions">
<ol>
<li class="action input_action " id="uploads_create_assets_former_submit_action">
<input type="submit" name="commit" value="Nächster Schritt" data-disable-with="Nächster Schritt" />
</li>
</ol>
</fieldset>
</form>
I have not deviated in any way from Active Storage direct upload documented at https://edgeguides.rubyonrails.org/active_storage_overview.html#direct-uploads. Upload of files starts on former submission.
Another edit:
I have prepared minimalistic Rails app where you can try to play with my issue: https://github.com/martintomas/capybara-direct-upload. I have double checked that path is correct (otherwise Capybara::FileNotFound is raised), tried relative and absolute paths. I have also checked that anybody can read file:
-rw-r--r-- 1 martintomas staff 26436 Oct 22 12:51 image.jpg
Same problem happens when tests are run on my local machine or inside CI environment. To be honest, I have run out of ideas so I have decided to go for hacky solution now.
Hacky solution:
If you absolute trust active storage direct upload implementation and you don't have extra js code related to direct upload, you can turn it off inside system tests.
def attach_file(locator = nil, paths, make_visible: nil, **options)
turn_off_direct_upload # Capybara does not work with direct upload
super
end
def turn_off_direct_upload
page.execute_script 'document.querySelectorAll("input[data-direct-upload-url]:not([data-direct-upload-url=\"\"])").forEach((input) => { delete input.dataset.directUploadUrl } )'
end

Capybara RSpec with CSS and JS?

rails (5.1.4)
rspec-rails (3.7.2)
capybara (2.16.1)
I'm trying to create a RSpec Rails 3.7 System spec as in https://relishapp.com/rspec/rspec-rails/v/3-7/docs/system-specs/system-spec .
Here my simple spec:
require 'rails_helper'
RSpec.describe "testing system", type: :system do
it "tests the spec" do
visit root_path
click_link 'Home'
save_and_open_page
end
The problem is that Capybara does render neither CSS content nor JS content after save_and_open_page call (in the browser) - just a plain HTML. The header inside this HTML-file contains some links
<link rel="stylesheet" media="all" href="/assets/application-ea5a1efcc44a908543519edabe00e74132151ebedeef3c1601921690d9162b5e.css" data-turbolinks-track="reload" />
<script src="/assets/application-ff63e43aef379fef744a00f21a8aadf96dc2ae8e612f8e7974b231f946569691.js" data-turbolinks-track="reload"></script>
but they reference some empty files.
Is there some way to fix it?
I tried some recipes, but still no luck. I tried to precompile the assets, to move "capybara.html" into the "public" folder, but no effect.
Modifying stylesheet_link_tag is not a good solution, a much better solution is to specify Capybara.asset_host which will add a <base> tag to any saved pages. Generally this would be set to something like
Capybara.asset_host = "http://localhost:3000/"
which would then load the JS/CSS assets from your dev server which would have access to the test mode compiled assets in the public subdirectory. Note: that none of this means the page will actually be functional since JS requests will still fail, DB records won't exist anymore, etc. Also, since it saves element attributes (not properties) a checkbox you just checked will probably not be checked in the saved page. However it will give you a generally styled page you can inspect the structure of. If all you're looking for is a current image of the page you should be using the save_screenshot/save_and_open_screenshot functionality provided by most of Capybaras drivers instead.
It has to do something with your assets.
Clear cache and run rake assets:clobber and rake assets:precompile
Still no luck, then check if Capybara is configured correctly.
Check app/views/layouts/application.html.erb has the correct Rails tags for stylesheets and javascripts. Something like this:
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_include_tag 'application' %>
On the command line, run:
rake assets:clobber
rake assets:precompile
Ensure that public/assets/ include:
.sprockets-manifest-<xyz>.json
application-<abc>.js
application-<def>.css
Open the .sprockets-manifest... file and you should see that there are application js and css files with filenames that match the actual public/assets/ files. This .sprockets-manifest file controls what actually gets included in the HTML head links and scripts when the Rails tags are replaced.
If this is still not working, ensure that the files are accessible by your user running the test (including the manifest). Occasionally lose the .sprockets-manifest file when copying files and in source control as it can appear to be hidden.
Finally, check your file log/test.log to see if there are any obvious errors being thrown during the tests.
I found a solution. Perhaps it's not the best one, but it works with me. If anybody find a better approach - let me know, please.
Run rake assets:precompile. I didn't even set RAILS_ENV=test.
Modify the stylesheet_link_tag method:
def stylesheet_link_tag2(*sources)
options = sources.extract_options!.stringify_keys
path_options = options.extract!('protocol').symbolize_keys
sources.uniq.map { |source|
tag_options = {
"rel" => "stylesheet",
"media" => "screen",
"href" => path_to_stylesheet(source, path_options)[1..-1]
}.merge!(options)
tag(:link, tag_options)
}.join("\n").html_safe
end
The idea is to turn the rendered link from this:
<link rel="stylesheet" media="all" href="/assets/application-ea5a1efcc44a908543519edabe00e74132151ebedeef3c1601921690d9162b5e.css" data-turbolinks-track="reload" />
to this:
<link rel="stylesheet" media="all" href="assets/application-ea5a1efcc44a908543519edabe00e74132151ebedeef3c1601921690d9162b5e.css" data-turbolinks-track="reload" />
eliminating the leading slash in the href attribute value (since we don't have a server running but just a saved HTML-page).
Replace the code inside the header in \app\views\layouts\application.html.erb to:
<% if Rails.env.test? %>
<%= stylesheet_link_tag2 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<% else %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<% end %>
Write a spec like this:
require 'rails_helper'
RSpec.describe "testing system", type: :system do
it "tests..." do
visit root_path
click_link 'Home'
save_and_open_page Rails.root.join( 'public', 'capybara.html' )
end
end
Add to .gitignore:
/public/capybara.html
Do the same thing with the JS-content.
UPDATE:
If you don't like modifying \app\views\layouts\application.html.erb you can do some monkey patching:
include ActionView::Helpers::AssetTagHelper
alias_method :old_stylesheet_link_tag, :stylesheet_link_tag
def stylesheet_link_tag2(*sources)
options = sources.extract_options!.stringify_keys
path_options = options.extract!('protocol').symbolize_keys
sources.uniq.map { |source|
tag_options = {
"rel" => "stylesheet",
"media" => "screen",
"href" => path_to_stylesheet(source, path_options)[1..-1]
}.merge!(options)
tag(:link, tag_options)
}.join("\n").html_safe
end
def stylesheet_link_tag(*sources)
if Rails.env.test?
stylesheet_link_tag2(*sources)
else
old_stylesheet_link_tag(*sources)
end
end
I usually put such code into app\helpers\application_helper.rb and add include ApplicationHelper into app\controllers\application_controller.rb
UPDATE 2
Setting Capybara.asset_host = "http://localhost:3000/" as #Thomas Walpole advised doesn't work. That's right - how can it work if http://localhost:3000/ is unavailable (AFTER the spec ran)? Of course - when I call save_and_open_page the HTML-file opens with a file://.... address - with no HTTP-server serving it. The attempts to set
Capybara.asset_host = "file://#{Rails.root}/public"
failed - looks like the base HTML-tag supports only http-adresses - not file://... ones. I checked it in Chrome and Firefox.
So my next code proposal is such:
include ActionView::Helpers::AssetTagHelper
alias_method :old_stylesheet_link_tag, :stylesheet_link_tag
def stylesheet_link_tag2(*sources)
options = sources.extract_options!.stringify_keys
path_options = options.extract!('protocol').symbolize_keys
sources.uniq.map { |source|
tag_options = {
"rel" => "stylesheet",
"media" => "screen",
"href" => "file://#{Rails.root}/public" + path_to_stylesheet(source, path_options)
}.merge!(options)
tag(:link, tag_options)
}.join("\n").html_safe
end
def stylesheet_link_tag(*sources)
if Rails.env.test?
stylesheet_link_tag2(*sources)
else
old_stylesheet_link_tag(*sources)
end
end
This eliminates the need to call
save_and_open_page Rails.root.join( 'public', 'capybara.html' )
instead you can simply call
save_and_open_page

How to include a manifest.json file in rails?

I have following html code in a view whose layout is false.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<link rel="manifest" href="manifest.json">
Jquery is being loaded but not the manifest.json, when I view page source and look at them I get this error:
No route matches [GET] "/manifest.json"
Even though I have included manifest.json inside the javascripts folder inside assets folder.
I also tried
<%= javascript_include_tag "manifest.json" %>
but that didn't work either..and that get routes error
I tried
<%= manifest_link_tag "manifest" %>
Again that didn't work out, gave error :
undefined method `manifest_link_tag'
I also added the manifest.json inside initializers/assets.rb but still no luck!!
I think you need to include it like this:
<link rel="manifest" href="<%= asset_path 'manifest.json' %>">
My solution is:
= tag(:link, rel: 'manifest', href: some_path)
or
= tag(:link, rel: 'manifest', href: asset_path('manifest.json'))

Rails mails sent with Amazon SES wrong format in Mac mail

I have a Rails webapp deployed in an Amazon EC2 instances that is using email templates in html with a header and logo and SES Amazon email service to send notification emails.
The emails are sent and correctly and they are displayed perfectly in all mail clientes (i.e. gmail, hotmail) even my mobile phone but in the Mac mail, where the logo is not displayed correctly.
Please, see a screenshot attached.
UPDATE:
I´m testing with Chrome and a Gmail account. If I open exactly same email in both (Mac mail and Chrome) I can see it right in Chrome, however, I see it wrong in Mail. I have checked the logo size is not been respected from CSS in Mail.
Please, see an screenshot from Gmail in Chrome. I see it right on mobiles too.
I attach the Rails code to generate the email:
In email_service.rb:
# booking request admin
def send_booking_request_admin_notification(customer, provider, boat, booking)
#customer = customer
#provider = provider
#boat = boat
#booking = booking
attachments.inline['logo.png'] = File.open("#{Rails.root.to_s + '/app/assets/images/logo.png'}", "rb") {|io| io.read}
mail(from: APP_CONFIG['YANPY_EMAIL_FROM'], to: APP_CONFIG['YANPY_EMAIL_FROM'], subject: t('booking_request_Yanpy_subject', user_email: customer.email , owner_email: provider.email, booking_code: booking.code))
end
In send_booking_request_admin_notification.erb:
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
<link rel="stylesheet" type="text/css" href="https://s3-eu-west-1.amazonaws.com/yanpy.dev/css/emails.css">
</head>
<body>
<div id="nav-bar">
<%= image_tag attachments['logo.png'].url, alt: 'Yanpy', size: '87x30', :class => "logo" %>
</div>
<div id="mail-content">
<h4><%= t('admin_hello') %></h4>
<p><%= t('booking_request_Yanpy_content_1', user_email: #customer.email, boat_name: #boat.name , owner_email: #provider.email, booking_code: #booking.code) %></p>
<p><%= t('admin_goodbye_1') %></p>
<p><%= t('admin_goodbye_2') %></p>
</div>
</body>
</html>
I have tried to reproduce the issue by resizing the browser Chrome windows in height and width but I couldn´t.
UPDATE 2:
I attach the relevent HTML code from Gmail in Chrome (working):
<div style="background:#f8f8f8;margin:0px;padding:0px" bgcolor="#f8f8f8">
<div style="background:#1b1b1b linear-gradient(to bottom,#222,#111);border:#252525;min-height:50px;width:100%">
<img alt="Yanpy" height="30" src="?ui=2&ik=836c81b94f&view=fimg&th=149e8a5ae9582fb1&attid=0.1&disp=emb&attbid=ANGjdJ8q_xSPAswkC2fDqsoAHPR0qKIUHUDjW45HorJli1u9zmcocdWFYXYi2rysx9cYcvsx7KJScrWLqDHBAOP-5W5g7LynDaj0cnMtQyS_P7D3A8ihhvpRKBLttmw&sz=w174-h60&ats=1416947426145&rm=149e8a5ae9582fb1&zw&atsh=1" width="87" style="margin-left:20px;margin-top:10px" class="CToWUd">
</div>
Now, from Mac Mail not working (note: I´m not sure if this is the real code, I don´t know how to get it from Mail. I just forwarded the email to a Hotmail account and got it from there):
<div style="background-color:rgb(248, 248, 248);padding:0px;font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;"><div style="background-color:rgb(27, 27, 27);border:rgb(37, 37, 37);height:50px;width:727px;background-position:initial initial;">
<img id="ecx52214002-0011-49E6-B577-6C08A8DA9C9E" height="121" width="352" src="https://dub126.afx.ms/att/GetInline.aspx?messageid=5293f1b7-74df-11e4-9417-00237de45652&attindex=0&cp=-1&attdepth=0&imgsrc=cid%3a32FDE1B1-DA1A-4A66-B63C-BBB067B5AB81%40Home&cid=ed5bb1624ed90a36&shared=1&hm__login=roberto.chingon&hm__domain=hotmail.com&ip=10.148.132.8&d=d3103&mf=0&hm__ts=Wed%2c%2026%20Nov%202014%2008%3a11%3a10%20GMT&st=roberto.chingon&hm__ha=01_745f8f086a98f87d549a0721ddd5738b4bfecce8795f549762672eea9f5cf5ba&oneredir=1"></div><div style="padding:20px;"><h4>Hola Admin,</h4><p>El usuario prueba se ha registrado con el email<span class="ecxApple-converted-space"> </span>propietario.yanpy3#gmail.com<span class="ecxApple-converted-space"> </span>correctamente.</p><p>Un saludo,</p><p>El equipo de Yanpy.</p></div></div>
Apple Mail uses WebKit rendering engine similar to Outlook for Mac, you should try to test it with WebKit browsers like Chrome and Safari.
I fixed it. Working with emails you have to develop like an old school HTML, I mean, you cannot link to external CSS files. Instead, you should add your css properties directly in the HTML template. Even better if it´s inline css in the style attribute of the tags.

Passing params to iframe in rails not working

I'm a rails noob so I know I'm probably totally missing something here. I'm trying to pass a url to an iframe through my products controller.
This is my setup.
Products Controller
def open_url
#url = params[:url]
end
index.html.erb
<%= link_to "More Info", open_path(url: "http://www.ceratoboutique.com" + product.destination_url) %>
open_url.html.erb
<iframe src= "<%= #url %>" style="border: 0; position:fixed; top:0; left:0; right:0; bottom:0; width:100%; height:100%" />
routes.rb
get '/open' => 'products#open_url', via: 'get'
I,ve checked out these two questions
Rails 4 - Passing Params via link_to?
Opening a Link in a New Window within an iFrame
but i'm still lost, the url is passed to the browser but it does not seem to pass to the #url variable in my controller.
Debug Dump
!ruby/hash:ActionController::Parameters
url: http://www.ceratoboutique.com/collections/tops/products/combo-blouse
controller: products
action: open_url
I decided to stick to rails conventions and make it a restful link. I still do not know why the original implementations did not work, but it worked using the show method in the controller.
Controller
def show
#url = Product.find(params[:id])
end
index.html.erb
<%= link_to "More Info", product_path(product) %>
show.html.erb
<iframe src= "<%= "http://www.ceratoboutique.com" + #product.destination_url %>" style="border: 0; position:fixed; top:0; left:0; right:0; bottom:0; width:100%; height:100%" />
****Edit Added More Info for including I-Frame ****
I ran into a lot of problems trying to get my iframe to work in chrome and on Heroku so I combined the process if anyone needs it ever. I first deployed to Heroku with full SSL running on my site, then realized that iframe did not work in chrome for sites that were not running SSL. I redeployed configuring force SSL to false, but heroku still forced my app to SSL. I realized that config.force_ssl = true enables Strict Transport Security header(HSTS) with max-age of one year, so I had to expire HSTS using the following.
Expire SSL in application controller
class ApplicationController < ActionController::Base
before_filter :expire_hsts
def expire_hsts
response.headers["Strict-Transport-Security"] = 'max-age=0'
end
In Production.rb
config.force_ssl = false
Then to make sure the x-frame showed in chrome browsers I added the following.
enable x-frame in chrome
config.action_dispatch.default_headers = { 'X-Frame-Options' => 'ALLOWALL' }
You may want to run SSL on some of your pages, which can be done rather easily via the SSL enforcer gem linked below.
ssl-enforcer gem
https://github.com/tobmatth/rack-ssl-enforcer
Best of luck on navigating the ugliness that is the iFrame!

Resources