make sure jQuery is loaded before account js

fixes: COREFE-349

this will make it so our javascript, specifically the global “$”
variable from jQuery, is loaded before custom account JS runs

test plan:
* you may have to test this in firefox or some other browser besides
  chrome because I could never reproduce the original problem in chrome
* upload a file as the custom javascript file in the theme editor with
  something like:
  $(function(){console.log(‘it worked’, $)})
* save that and apply it to your account
* you should see the it worked message always logged to your console
  and should not ever see a “$ is undefined” error in the console

Once this is on beta:
* we should specifically test the schools that we know had this problem
  originally and see if it is resolved for them

Change-Id: Ic0f245cb649f3cf0148f4b0c9b263aa3b46aad57
Reviewed-on: https://gerrit.instructure.com/212390
Tested-by: Jenkins
Reviewed-by: Clay Diffrient <cdiffrient@instructure.com>
QA-Review: Ryan Shaw <ryan@instructure.com>
Product-Review: Ryan Shaw <ryan@instructure.com>
This commit is contained in:
Ryan Shaw 2019-10-07 21:16:08 -06:00
parent 69b7443df7
commit 05a477e1c8
2 changed files with 51 additions and 19 deletions

View File

@ -170,6 +170,19 @@ module ApplicationHelper
(use_optimized_js? ? '/dist/webpack-production' : '/dist/webpack-dev').freeze (use_optimized_js? ? '/dist/webpack-production' : '/dist/webpack-dev').freeze
end end
def load_scripts_async_in_order(script_urls)
# this is how you execute scripts in order, in a way that doesnt block rendering,
# and without having to use 'defer' to wait until the whole DOM is loaded.
# see: https://www.html5rocks.com/en/tutorials/speed/script-loading/
javascript_tag "
;#{script_urls.map{ |url| javascript_path(url)}}.forEach(function(src) {
var s = document.createElement('script')
s.src = src
s.async = false
document.head.appendChild(s)
});"
end
# puts the "main" webpack entry and the moment & timezone files in the <head> of the document # puts the "main" webpack entry and the moment & timezone files in the <head> of the document
def include_head_js def include_head_js
paths = [] paths = []
@ -197,17 +210,8 @@ module ApplicationHelper
paths.each { |url| concat preload_link_tag(javascript_path(url)) } paths.each { |url| concat preload_link_tag(javascript_path(url)) }
chunk_urls.each { |url| concat preload_link_tag(url) } chunk_urls.each { |url| concat preload_link_tag(url) }
# this is how you execute scripts in order, in a way that doesnt block rendering,
# and without having to use 'defer' to wait until the whole DOM is loaded. concat load_scripts_async_in_order(paths + chunk_urls)
# see: https://www.html5rocks.com/en/tutorials/speed/script-loading/
concat javascript_tag "
;#{(paths + chunk_urls).map{ |url| javascript_path(url)}}.forEach(function(src) {
var s = document.createElement('script')
s.src = src
s.async = false
document.head.appendChild(s)
});
"
concat include_js_bundles concat include_js_bundles
end end
end end
@ -727,7 +731,9 @@ module ApplicationHelper
end end
if includes.present? if includes.present?
javascript_include_tag(*includes, defer: true) # Loading them like this puts them in the same queue as our script tags we load in
# include_head_js. We need that because we need them to load _after_ our jquery loads.
load_scripts_async_in_order(includes)
end end
end end

View File

@ -367,14 +367,34 @@ describe ApplicationHelper do
it "should just include domain root account's when there is no context or @current_user" do it "should just include domain root account's when there is no context or @current_user" do
output = helper.include_account_js output = helper.include_account_js
expect(output).to have_tag 'script' expect(output).to have_tag 'script'
expect(output).to eq("<script src=\"https://example.com/root/account.js\" defer=\"defer\"></script>") expect(output).to eq("<script>
//<![CDATA[
;[\"https://example.com/root/account.js\"].forEach(function(src) {
var s = document.createElement('script')
s.src = src
s.async = false
document.head.appendChild(s)
});
//]]>
</script>")
end end
it "should load custom js even for high contrast users" do it "should load custom js even for high contrast users" do
@current_user = user_factory @current_user = user_factory
user_factory.enable_feature!(:high_contrast) user_factory.enable_feature!(:high_contrast)
output = helper.include_account_js output = helper.include_account_js
expect(output).to eq("<script src=\"https://example.com/root/account.js\" defer=\"defer\"></script>") expect(output).to eq("<script>
//<![CDATA[
;[\"https://example.com/root/account.js\"].forEach(function(src) {
var s = document.createElement('script')
s.src = src
s.async = false
document.head.appendChild(s)
});
//]]>
</script>")
end end
it "should include granchild, child, and root when viewing the grandchild or any course or group in it" do it "should include granchild, child, and root when viewing the grandchild or any course or group in it" do
@ -382,11 +402,17 @@ describe ApplicationHelper do
group = course.groups.create! group = course.groups.create!
[@grandchild_account, course, group].each do |context| [@grandchild_account, course, group].each do |context|
@context = context @context = context
expect(helper.include_account_js).to eq %{ expect(helper.include_account_js).to eq("<script>
<script src="https://example.com/root/account.js" defer="defer"></script> //<![CDATA[
<script src="https://example.com/child/account.js" defer="defer"></script>
<script src="https://example.com/grandchild/account.js" defer="defer"></script> ;[\"https://example.com/root/account.js\", \"https://example.com/child/account.js\", \"https://example.com/grandchild/account.js\"].forEach(function(src) {
}.strip var s = document.createElement('script')
s.src = src
s.async = false
document.head.appendChild(s)
});
//]]>
</script>")
end end
end end
end end