Capybara RSpec with CSS and JS? - ruby-on-rails

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

Related

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'))

Getting Dojo to Work with Ruby on Rails 4

I'm porting my symfony app to Ruby on Rails 4.2.0. My setup works fine in symfony. There is this old post on how to use dojo with RoR, but it uses deprecated code.
In my application.html.erb I have
<script>dojoConfig = {async: true}</script>
<%= javascript_include_tag 'http://ajax.googleapis.com/ajax/libs/dojo/1.10.3/dojo/dojo.js'%>
I replaced the symfony wrappers with Rails ones. I also changed the dojo version. I was using 1.9.1. Rails generates this html:
<script>
dojoConfig = {async: true}
</script>
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.10.3/dojo/dojo.js">
The last line is followed by a bunch of compressed javascript and the close script tag in Firebug.
I didn't make any changes in my home/index.html.erb where I'm testing this code. In app/assets/javaascipts/home.js, I have:
//require(["dojo/dom", "dojo/ready", "dijit/Tooltip"], function(dom, ready, Tooltip)
define(["dojo/dom", "dojo/ready", "dijit/Tooltip"], function(dom, ready, Tooltip)
{
ready(function()
{
var head = "<div class='footnote-text'>";
var tail = "</div>";
var fnt1 = head + dom.byId("fnb1").innerHTML + tail;
var fnt2 = head + dom.byId("fnb2").innerHTML + tail;
var fnt4 = head + dom.byId("fnb4").innerHTML + tail;
new Tooltip({ connectId: ["footnote1"],position:["after","above","below"],label: fnt1 });
new Tooltip({ connectId: ["footnote2"],position:["after","above","below"],label: fnt2 });
new Tooltip({ connectId: ["footnote4"],position:["after","above","below"],label: fnt4 });
new Tooltip({ connectId: ["footnote5"],position:["after","above","below"],label: fnt4 });
});
});
//require(["dojo/dom", "dojo/ready", "dijit/Dialog"], function(dom, ready, Dialog){
define(["dojo/dom", "dojo/ready", "dijit/Dialog"], function(dom, ready, Dialog){
ready(function(){
var fnt3 = dom.byId("fnb3").innerHTML;
myDialog = new Dialog({
title: "Contact Me",
content: fnt3,
style: "width: 300px"
});
});
});
The commented out require lines are what I use in my symfony app. As you can see, I replaced them with define as described on the dojo site. When I run it, I get the following error on the Firebug console:
ReferenceError: define is not defined
...define(["dojo/dom", "dojo/ready", "dijit/Tooltip"], function(dom, ready, Tooltip
If I used require instead of define, I get require is not defined.
Update
I tried installing dojo into the app and made some progress. I copied the download from dojo to vendor/assets/javascript/dojo. The dojo directory contains the subdirectories dojo, dojox, and digit
I then added
//= require dojo/dojo/dojo.js
to app/assets/javascript/application.js. I also changed the define back to require in the home.js file. When I reloaded the page, I got an error complaining that it couldn't find Tooltip. I then added:
//= require dojo/dijit/Tooltip.js
On reload it complained about a bunch of other missing js files. This is the same problem I had using symfony, which is why I went to the google image. How can I get to rails to search for the files in the vendor directories? This is one of the errors:
"NetworkError: 404 Not Found - http://amcolan.loc/dijit/_base/manager.js"
Update 2
Since require_tree worked for app assets, I thought it might work to vendor as well. I added
//= require_tree ../../../vendor/assets/javascripts/dojo
to my application.js file. When I reloaded the page, it took about a minute. My guess is that it's loading everything in the dojo directory tree, which is not surprising. The page load completed without any errors. When I hovered over a tooltip item (the purpose of the code is to show tooltips), Firebug cranked out about two thousand errors and quit. All the errors appear to be "ReferenceError: define is not defined"
Update 3
I went back to using the googleapi. My application.html.erb header looks like this
<head>
<meta charset="UTF-8">
<title><%= content_for?(:title) ? yield(:title) : "American Colonial Ancestors" %></title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<script>dojoConfig = {async: true}</script>
<%= javascript_include_tag 'http://ajax.googleapis.com/ajax/libs/dojo/1.10.3/dojo/dojo.js'%>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
I reversed the order of the javascript includes. The page reloaded without errors. The tooltip doesn't work, but it doesn't generate any errors when I hover over an item. I put a bad statement in the home.js code and it came up on the console so I know the code is being parsed. I may just have a bug in my page setup.
There may be more than one way to get Dojo toolkit to work with Ruby on Rails. This is the easiest if not the most efficient way. This works in Rails 4.2.0. I would imagine it would work in other versions as well.
In views/layouts/application.html.erb add the following prior to the inclusion of the site scripts:
<script>dojoConfig = {async: true}</script>
<%= javascript_include_tag 'http://ajax.googleapis.com/ajax/libs/dojo/1.10.3/dojo/dojo.js'%>
Change the version to the latest or to which ever one you want to use. Here I'm using version 1.10.3. The dojo site says there are other CDN's (Content Delivery Network) for the source code. I'm using google as it was in their example. Here are the pertinent parts of my head section:
<head>
<meta charset="UTF-8">
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= stylesheet_link_tag "http://ajax.googleapis.com/ajax/libs/dojo/1.10.3/dijit/themes/claro/claro.css" %>
<script>dojoConfig = {async: true}</script>
<%= javascript_include_tag 'http://ajax.googleapis.com/ajax/libs/dojo/1.10.3/dojo/dojo.js'%>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= content_for :page_script %>
<%= csrf_meta_tags %>
</head>
If you are going to use any of the toolkit's dialog boxes, tooltips, etc, you will need to include a stytlesheet by adding something like this:
<%= stylesheet_link_tag "http://ajax.googleapis.com/ajax/libs/dojo/1.10.3/dijit/themes/claro/claro.css" %>
Change the version and the theme to your own requirements. Here I'm using the claro theme. You can see it in my head section above. I don't think placement is critical. You also need to declare your theme class in the body statement. Here's mine:
<body class="claro">
An older post on the subject had different javascript formatting. I don't think anything special is needed. Here's an example of a working script:
require(["dojo/dom", "dojo/ready", "dijit/Tooltip"], function(dom, ready, Tooltip)
{
ready(function()
{
var head = "<div class='footnote-text'>";
var tail = "</div>";
var fnt1 = head + dom.byId("fnb1").innerHTML + tail;
var fnt2 = head + dom.byId("fnb2").innerHTML + tail;
new Tooltip({connectId: ["footnote1"], position:["after","above","below"], label: fnt1 });
new Tooltip({connectId: ["footnote2"], position:["after","above","below"], label: fnt2 });
});
});
As mentioned in my question, I tried placing the Dojo Toolkit source in vendor/assets/javascript. Starting with with version 1.7, dojo started using Asynchronous Module Definition (AMD). It may be the case that the AMD loader is incompatible with the Rails pre-compile feature. I don't know enough about it to say for sure.

Include all javascript files from application except a specific folder in Rails 3.2.11

I would like to exclude a specific folder from assets/javascripts in my application.html.haml
The folder that I do not want to be included in my layout is mobile folder
Here's my layout:
%html(lang="en")
%head
%meta(charset="utf-8")
%meta(http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1")
%meta(name="viewport" content="width=device-width, initial-scale=1.0")
%title= content_for?(:title) ? yield(:title) : #user.name ? 'Niche | ' + #user.name : 'Niche'
= csrf_meta_tags
= analytics_init if Rails.env.production?
/ Le HTML5 shim, for IE6-8 support of HTML elements
/[if lt IE 9]
= javascript_include_tag "//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.6.1/html5shiv.js"
= stylesheet_link_tag "application", :media => "all"
%body#profile
%section.persist-area
%header.global.profile
%div.container
%a.brand.pull-left{ :href => root_path }
.logo-niche
%nav.pull-right
= render 'layouts/menu'
.container-fluid#main
= yield :profile
/
Javascripts
\==================================================
/ Placed at the end of the document so the pages load faster
= javascript_include_tag "application"
= yield :javascript
In this snippet, I am getting all JS:
= javascript_include_tag "application"
But I want to get all JS except the folder named mobile
I tried the following workaround but did not work:
= javascript_include_tag "application", except: "mobile"
Sorry that was just a wild guess. There's no javascript_exclude_tag too as per the docs.
Any ideas? Thanks.
PS: I have a different layout for mobile, so that's why I really want to exclude that folder in desktop view. It's conflicting a lot of conflicts in my desktop view.
Change in application.js
//= require_tree
to
//= require_directory .
or
//= require_tree ./useful
So that, app/assets/javascripts/* files will be included and app/assets/javascripts/mobile/* wont be included.

Using asset pipeline outside of ERB

Is there a way to use the rails asset pipeline outside of erg? When I call stylesheet_link_tag(), I get a normal /stylesheets/ link instead of an /assets/ like I'd expect. I suspect that the stache gem just needs to register something with the asset pipeline, but I'm not sure what.
I'm using this gem: https://github.com/agoragames/stache
The code I'm using:
module Layouts
class Application < ::Stache::View
include ActionView::Helpers::AssetTagHelper::StylesheetTagHelpers
def title
'foobar'
end
def stylesheets
[
[stylesheet_link_tag('reset', :media => 'all')]
]
end
def javascripts
end
end
end
It's generating:
<link href="/stylesheets/reset.css" media="all" rel="stylesheet" type="text/css" />
It should be generating (it does this in erb templates):
<link href="/assets/reset.css?body=1" media="all" rel="stylesheet" type="text/css" />
Using rails 3.2.3.
Try
def stylesheets
[
[stylesheet_link_tag("#{ActionController::Base.helpers.asset_path('reset.css')}", :media => 'all')]
]
end
also read https://stackoverflow.com/a/9341764/643500
The proper solution is to remove the:
include ActionView::Helpers::AssetTagHelper::StylesheetTagHelpers
line at the top.

Problems Rendering View (ActionView::MissingTemplate ... Error) in Custom Plugin

I am trying to develop a plugin for Ruby on Rails and came across problems rendering my html view. My directory structure looks like so:
File Structure
---/vendor
|---/plugins
|---/todo
|---/lib
|---/app
|---/controllers
|---todos_controller.rb
|---/models
|---todos.rb
|---/views
|---index.html.erb
|---todo_lib.rb
|---/rails
|---init.rb
In /rails/init.rb
require 'todo_lib'
In /lib/app/todo_lib.rb
%w{ models controllers views }.each do |dir|
# Include the paths:
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/lib/app/models
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/lib/app/controllers
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/lib/app/views
path = File.expand_path(File.join(File.dirname(__FILE__), 'app', dir))
# We add the above path to be included when Rails boots up
$LOAD_PATH << path
ActiveSupport::Dependencies.load_paths << path
ActiveSupport::Dependencies.load_once_paths.delete(path)
end
In todo/lib/app/controllers/todos_controller.rb
class TodosController < ActionController::Base
def index
end
end
In todo/lib/app/views/index.html.erb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"[url]http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd[/url]">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>Todos:</title>
</head>
<body>
<p style="color: green" id="flash_notice"><%= flash[:notice] %></p>
<h1>Listing Todos</h1>
</body>
</html>
In /myRailsApp/config/routes.rb
ActionController::Routing::Routes.draw do |map|
# The priority is based upon order of creation: first created -> highest priority.
map.resources :todos
...
The error I get is the following:
Template is missing
Missing template todos/index.erb in view path app/views
Can anyone give me a hand up and tell me what am I doing wrong here that is causing my index.html.erb file to not render? Much appreciated!
EDIT:
I have already tried the following without success:
In /todo/lib/app/controllers/todos_controller.rb
def index
respond_to do |format|
format.html # index.html.erb
end
end
EDIT:
hakunin solved this problem. Here's the solution.
He says that I'm building a Rails engine plugin (I had no idea I was doing this), and it requires a different directory structure, one that appears like so:
File Structure
---/vendor
|---/plugins
|---/todo
|---/lib
|---/app
|---/controllers
|---todos_controller.rb
|---/models
|---todos.rb
|---/views
|---/todos
|---index.html.erb
|---todo_lib.rb
|---/rails
|---init.rb
This required the following changes:
In todo/lib/todo_lib.rb
%w{ models controllers views }.each do |dir|
# Include the paths:
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/app/models
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/app/controllers
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/app/views
path = File.expand_path(File.join(File.dirname(__FILE__), '../app', dir))
# We add the above path to be included when Rails boots up
$LOAD_PATH << path
ActiveSupport::Dependencies.load_paths << path
ActiveSupport::Dependencies.load_once_paths.delete(path)
end
The change made above is in the line: path = File.expand_path(File.join(File.dirname(FILE), '../app', dir)). [Ignore the boldened 'FILE', this is an issue with the website].
Running script/server will render the index.html.erb page under todo/app/views/todos.
Looks like you want to build an "engine" plugin. Create "app" and "config" dirs in the root of your plugin dir (not under /lib). You can use app/views/ and app/controllers in your plugin as if it was a full featured Rails app. In config/routes.rb you should declare routes introduced by your engine.
See http://github.com/neerajdotname/admin_data for a decent example of what engine looks like.

Resources