I am unable to get jasmine to load my custom javascript file, even though it works perfectly in the browser. I've reduced the javascript to the minimum to avoid any possibility of errors and I still get a failing test on the simplest thing.
Custom ARB.js file:
$(document).ready(function() {
alert('typeof ARB: ' + typeof ARB);
});
ARB = {};
ARB.VERSION = "V1.01.00 2012-08-24";
jasmine configuration file snippet (I'm on Rails 3.0.9):
src_files:
- "public/javascripts/**/*.js"
- "public/javascripts/ARB.js"
This test:
it('should define the custom javascript functions', function() {
expect(typeof ARB).toEqual('object');
});
fails with:
Expected 'undefined' to equal 'object'.
jQuery gets loaded and so does my application.js file. When running jasmine, I get no alert message, but I do when I run my app.
What am I missing?
UPDATE: If I remove the $(document).ready function, jasmine passes all the tests - what's that all about?
shioyama gave me the pointer that I needed to figure this out: my custom ARB.js file was getting loaded before the jquery files so it didn't have access to the $(document).ready function. What I had to do was explicitly spell out the order in which my javascript files were to be loaded. Here's what I put in my jasmine config file at /spec/javascripts/support/jasmine.yml:
src_files:
- "public/javascripts/**/jquery.js"
- "public/javascripts/**/jq*.js"
- "public/javascripts/**/application.js"
- "app/assets/javascripts/**/*.js"
- "public/javascripts/ARB.js"
I first force the main jquery.js file to load, then all the other jquery files, then application.js, then any other javascript files that are located in the assets directory, and finally my custom javascript file.
This works for me because I'm still on Rails 3.0.9 and starting the migration to 3.1+ and the asset pipeline.
Related
I did just a upgrade from RAILS 5 to RAILS 6 and I see that all rails views are not able to call a javascript function as before in RAILS 5.
I have an external javascript file located under
app/javascript/packs/station.js
This is is embeded in in app/views/layouts/application.html.erb as
<%= javascript_pack_tag 'station' %>
This is the code how I call the javascrpt function from html.erb file :
<%= text_field_tag(:station_text_field, ... ,
onkeyup: "javascript: request_stations(); ") %>
When I try to call a function thats is part of the station.js then I get an error in the browser developmer view: ReferenceError: request_stations is not defined
But I can also see in the brwoser view, under Debugger :
Webpack / app/javascript / packs / station.js
and the javascript function I want to call.
So it seems that this script was loaded by the browser.
In contrast, when I just copy and paste these few lines that represent this javascript function direct into the template view file (...html.erb), something like :
<script>
function request_stations ()
{
alert("calling request_stations");
};
</script>
then - it works as expected !
By default, variables/functions defined inside JavaScript files that are packed by Webpacker will not be available globally.
This is a good thing, because it prevents global naming conflicts. Generally speaking, you don't want to reference javascript functions/variables from your view. You instead want to write JavaScript in a way that attaches functionality to DOM nodes using their id or other attributes.
Here is a basic example based on the code you provided:
# in your rails view
<%= text_field_tag(:station_text_field, ..., id: 'station-text-field') %>
// in your javascript
function request_stations() {
alert("calling request_stations");
};
const stationTextField = document.querySelector("#station-text-field");
stationTextField.addEventListener('keyup', (event) => {
request_stations();
});
Agree with mhunter's answer.
This post helped me get a grounding on this difference in Rails 6: https://blog.capsens.eu/how-to-write-javascript-in-rails-6-webpacker-yarn-and-sprockets-cdf990387463
What I don't see in your question is whether or not you did this in app/javascript/packs/application.js:
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
require("station")
The big difference in Rails 6 is that you have to deliberately:
require a JS file
deliberately export something from that file
deliberately import that something, in the file where you want to use it.
So if there is a function in station.js that you want to use, connect the steps above. Start with a simple function in station.js that fires upon DOMContentLoaded, and add a console.log("hey, station.js is alive and well"). If you don't see it, then something in those 3 steps is not right.
In pre-Rails6, you had a "garden" of JavaScript, just by virtue of being in the asset pipeline. In Rails 6, you have to be more deliberate.
For the sake of debugging the javascript-part of a Rails 6 (version 6.0.0.rc1) web application I want to use my custom javascript functions also in the Chrome console (aka. Inspect).
Back when I used just static HTML files to build a website (as opposed to using a web-framework like Rails as of right now) you would simply embed the JS file in the DOM like this
<!-- custom JS -->
<script src="js/custom.js"></script>
and could instantly access and execute all custom functions that were placed in this file.
Background:
The JS file is placed at the correct rails 6 specific directory as provided in this article: How to require custom JS files in Rails 6
Note:
The rails 6 application also uses the JS file already, since the browser shows the console log message.
Here is the full content of the JS file:
// app/javascript/packs/custom.js
console.log("loaded custom.js successfully")
function sayHello () {
console.log("Hello good Sir or Madam!")
}
Expectation: I am expecting to open the browser's (Chrome) console and be able to use the sayHello() function in the console.
However, when I do so, I get an error message in the console stating:
Uncaught ReferenceError: sayHello is not defined
Try something like
sayHello = ()=>{
console.log("Hello good Sir or Madam!");
}
then you can evoke in console:
>sayHello();
When using the karma javascript test library (née Testacular) together with Rails, where should test files and mocked data go be placed?
It seems weird to have them in /assets/ because we don’t actually want to serve them to users. (But I guess if they are simply never precompiled, then that’s not an actual problem, right?)
Via this post: https://groups.google.com/forum/#!topic/angular/Mg8YjKWbEJ8
I'm experimenting with something that looks like this:
// list of files / patterns to load in the browser
files: [
'http://localhost:3000/assets/application.js',
'spec/javascripts/*_spec.coffee',
{
pattern: 'app/assets/javascripts/*.{js,coffee}',
watched: true,
included: false,
served: false
}
],
It watches app js files, but doesn't include them or serve them, instead including the application.js served by rails and sprockets.
I've also been fiddling with https://github.com/lucaong/sprockets-chain , but haven't found a way to use requirejs to include js files from within gems (such as jquery-rails or angularjs-rails).
We ended up putting tests and mocked data under the Rails app’s spec folder and configuring Karma to import them as well as our tested code from app/assets.
Works for us. Other thoughts are welcome.
Our config/karma.conf.js file:
basePath = '../';
files = [
JASMINE,
JASMINE_ADAPTER,
//libs
'vendor/assets/javascripts/angular/angular.js',
'vendor/assets/javascripts/angular/angular-*.js',
'vendor/assets/javascripts/jquery-1.9.1.min.js',
'vendor/assets/javascripts/underscore-min.js',
'vendor/assets/javascripts/angular-strap/angular-strap.min.js',
'vendor/assets/javascripts/angular-ui/angular-ui.js',
'vendor/assets/javascripts/angular-bootstrap/ui-bootstrap-0.2.0.min.js',
//our app!
'app/assets/javascripts/<our-mini-app>/**',
// and our tests
'spec/javascripts/<our-mini-app>/lib/angular/angular-mocks.js',
'spec/javascripts/<our-mini-app>/unit/*.coffee',
// mocked data
'spec/javascripts/<our-mini-app>/mocked-data/<data-file>.js.coffee',
];
autoWatch = true;
browsers = 'PhantomJS'.split(' ')
preprocessors = {
'**/*.coffee': 'coffee'
}
I found this project helpful as a starting point. https://github.com/monterail/rails-angular-karma-example. It is explained by the authors on their blog.
It's an example rails app with angular.js and karma test runner.
I'm struggling with the asset pipeline. I'm loading dojo from Google CDN putting this in my template:
= javascript_include_tag 'http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dojo/dojo.xd.js', :'data-dojo-config' => %Q(dojoBlankHtmlUrl:'/blank.html', baseUrl: 'assets/', modulePaths: {custom: 'javascripts/modules'})
I just want a fallback to a local version if running locally or if the CDN is down. I thought of doing this:
script typeof(dojo) === "undefined" && document.write(unescape('%3Cscript src="js/libs/dojo-1.6.1.min.js"%3E%3C/script%3E'));
But I don't like it as it works out of the asset pipeline. I want to keep dojo in vendors/assets/javascripts/dojo. How can I get the fallback to be served by the asset pipeline.
Is there a way do declare conditional require in the asset pipeline. What I want is to run some javascript tests, and depending on the result serve a file.
Thanks
I suggest you use yepnope, a lightweight library for loading libraries like this in parallel (for speed) and it gives you the option to run some other code to test if the library is loaded. For example:
yepnope([{
load: 'http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dojo/dojo.xd.js',
complete: function () {
if (!window.jQuery) {
yepnope('asset_path('you_local_copy_of_dojo') ');
}
}
}])
(Note: You will need erb tags around the asset_path helper)
The local dojo file would be in the assets/javascript folder, but not included in the application manifest. You need to add the dojo file to the precompile array:
config.assets.precompile += 'your_local_file.js'
And this will make it available to the asset_path helper.
Thanks Richard!
I don't want to have yepnope to load one library. It would be overkill imo. Here is the solution I came up with, based on your help (written in slim):
1/ In vendors/assets/javascripts/, I have my dojo.js.
2/ In config/application.rb:
# Precompile these assets files
config.assets.precompile += ['dojo.js']
3/ In the template:
= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/dojo/#{Settings.dojoVersion}/dojo/dojo.xd.js", :'data-dojo-config' => %Q(dojoBlankHtmlUrl:'/blank.html', baseUrl: 'assets/', modulePaths: {custom: 'javascripts/modules'})
script = "typeof(dojo) === \"undefined\" && document.write(unescape('%3Cscript src=\"#{asset_path('dojo')}\"%3E%3C/script%3E'));".html_safe
I also posted on the Rails Google Group to request the addition of two options to the javascript_include_tag, :test and :local that would take care of all the work. We'll see.
If I go to http://localhost:3000/assets/application.js my code (which works fine in 3.0) exists, because I've referenced it fine in the new application.js assets pipeline file:
$(document).ready
(function(){
$('input.ui-date-picker').datepicker({
dateFormat: 'dd-mm-yy'
});
});
But it's not being called. Jquery is present, too, and my gemfile got upgraded ok. What could be wrong?
OK, the problem was that I had a number of bits of code within a .js file I'd called various.js, and not all of them were wrapped in $(document).ready... }); Once I added that to each separate bit of code it worked fine. Also, I have to restart the server each time I make a change to the .js file - just touching it doesn't work for me. I found a simple Hello World alert really useful in debugging this (just in case anyone out there is as new to jquery as me!):
$(document).ready(function() {
alert("Hello world!")
});