diff --git a/Guardfile b/Guardfile
new file mode 100644
index 000000000..7f999bb18
--- /dev/null
+++ b/Guardfile
@@ -0,0 +1,6 @@
+guard :minitest do
+ # with Minitest::Unit
+ watch(%r{^test/(.*)\/?(.*)_test\.rb$})
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" }
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
+end
diff --git a/README.md b/README.md
index 0acb98390..4f5f51ce3 100644
--- a/README.md
+++ b/README.md
@@ -154,18 +154,14 @@ You can configure your pool of JS virtual machines and specify where it should l
# config/environments/application.rb
# These are the defaults if you dont specify any yourself
MyApp::Application.configure do
- # renderer pool size:
- config.react.max_renderers = 10
- # prerender timeout, in seconds:
- config.react.timeout = 20
- # where to get React.js source:
- config.react.react_js = lambda { File.read(::Rails.application.assets.resolve('react.js')) }
- # array of filenames that will be requested from the asset pipeline
- # and concatenated:
- config.react.component_filenames = ['components.js']
- # server-side console.log, console.warn, and console.error messages will be replayed on the client
- # (you can set this to `true` in config/enviroments/development.rb to replay in development only)
- config.react.replay_console = false
+ # Settings for the pool of renderers:
+ config.react.server_renderer_pool_size ||= 10
+ config.react.server_renderer_timeout ||= 20 # seconds
+ config.react.server_renderer = React::ServerRendering::SprocketsRenderer
+ config.react.server_renderer_options = {
+ files: ["react.js", "components.js"], # files to load for prerendering
+ replay_console: true, # if true, console.* will be replayed client-side
+ }
end
```
diff --git a/lib/react-rails.rb b/lib/react-rails.rb
index 9e75b37ab..fdb8abf93 100644
--- a/lib/react-rails.rb
+++ b/lib/react-rails.rb
@@ -1,5 +1,4 @@
require 'react/jsx'
-require 'react/renderer'
require 'react/rails'
-require 'react/console'
+require 'react/server_rendering'
diff --git a/lib/react/console.rb b/lib/react/console.rb
deleted file mode 100644
index 35050fe97..000000000
--- a/lib/react/console.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-module React
- class Console
- def self.polyfill_js
- # Overwrite global `console` object with something that can capture messages
- # to return to client later for debugging
- <<-JS
- var console = { history: [] };
- ['error', 'log', 'info', 'warn'].forEach(function (fn) {
- console[fn] = function () {
- console.history.push({level: fn, arguments: Array.prototype.slice.call(arguments)});
- };
- });
- JS
- end
-
- def self.replay_as_script_js
- <<-JS
- (function (history) {
- if (history && history.length > 0) {
- result += '\\n';
- history.forEach(function (msg) {
- result += '\\nconsole.' + msg.level + '.apply(console, ' + JSON.stringify(msg.arguments) + ');';
- });
- result += '\\n';
- }
- })(console.history);
- JS
- end
- end
-end
diff --git a/lib/react/rails/railtie.rb b/lib/react/rails/railtie.rb
index 362e3685f..855f80217 100644
--- a/lib/react/rails/railtie.rb
+++ b/lib/react/rails/railtie.rb
@@ -9,11 +9,11 @@ class Railtie < ::Rails::Railtie
config.react.variant = (::Rails.env.production? ? :production : :development)
config.react.addons = false
config.react.jsx_transform_options = {}
- # Server-side rendering
- config.react.max_renderers = 10
- config.react.timeout = 20 #seconds
- config.react.react_js = lambda {File.read(::Rails.application.assets.resolve('react.js'))}
- config.react.component_filenames = ['components.js']
+ # Server rendering:
+ config.react.server_renderer_pool_size = 10
+ config.react.server_renderer_timeout = 20 # seconds
+ config.react.server_renderer = nil # defaults to SprocketsRenderer
+ config.react.server_renderer_options = {} # SprocketsRenderer provides defaults
# Watch .jsx files for changes in dev, so we can reload the JS VMs with the new JS code.
initializer "react_rails.add_watchable_files", group: :all do |app|
@@ -49,24 +49,17 @@ class Railtie < ::Rails::Railtie
"react-#{variant}",
].compact.join('-')
- # Server Rendering
- # Concat component_filenames together for server rendering
- app.config.react.components_js = lambda {
- app.config.react.component_filenames.map do |filename|
- app.assets[filename].to_s
- end.join(";")
- }
+ # The class isn't accessible in the configure block, so assign it here if it wasn't overridden:
+ app.config.react.server_renderer ||= React::ServerRendering::SprocketsRenderer
- do_setup = lambda do
- cfg = app.config.react
- React::Renderer.setup!( cfg.react_js, cfg.components_js, cfg.replay_console,
- {:size => cfg.max_renderers, :timeout => cfg.timeout})
- end
-
- do_setup.call
+ React::ServerRendering.pool_size = app.config.react.server_renderer_pool_size
+ React::ServerRendering.pool_timeout = app.config.react.server_renderer_timeout
+ React::ServerRendering.renderer_options = app.config.react.server_renderer_options
+ React::ServerRendering.renderer = app.config.react.server_renderer
- # Reload the JS VMs in dev when files change
- ActionDispatch::Reloader.to_prepare(&do_setup)
+ React::ServerRendering.reset_pool
+ # Reload renderers in dev when files change
+ ActionDispatch::Reloader.to_prepare { React::ServerRendering.reset_pool }
end
end
end
diff --git a/lib/react/rails/view_helper.rb b/lib/react/rails/view_helper.rb
index 5abb55e7a..992659230 100644
--- a/lib/react/rails/view_helper.rb
+++ b/lib/react/rails/view_helper.rb
@@ -1,28 +1,29 @@
module React
module Rails
module ViewHelper
-
# Render a UJS-type HTML tag annotated with data attributes, which
# are used by react_ujs to actually instantiate the React component
# on the client.
- #
- def react_component(name, args = {}, options = {}, &block)
+ def react_component(name, props = {}, options = {}, &block)
options = {:tag => options} if options.is_a?(Symbol)
- block = Proc.new{concat React::Renderer.render(name, args)} if options[:prerender]
+
+ prerender_options = options[:prerender]
+ if prerender_options
+ block = Proc.new{ concat React::ServerRendering.render(name, props.merge(prerender: prerender_options)) }
+ end
html_options = options.reverse_merge(:data => {})
html_options[:data].tap do |data|
data[:react_class] = name
- data[:react_props] = React::Renderer.react_props(args) unless args.empty?
+ data[:react_props] = (props.is_a?(String) ? props : props.to_json)
end
html_tag = html_options[:tag] || :div
-
+
# remove internally used properties so they aren't rendered to DOM
html_options.except!(:tag, :prerender)
-
+
content_tag(html_tag, '', html_options, &block)
end
-
end
end
end
diff --git a/lib/react/renderer.rb b/lib/react/renderer.rb
deleted file mode 100644
index e1315695c..000000000
--- a/lib/react/renderer.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-require 'connection_pool'
-
-module React
- class Renderer
-
- class PrerenderError < RuntimeError
- def initialize(component_name, props, js_message)
- message = ["Encountered error \"#{js_message}\" when prerendering #{component_name} with #{props}",
- js_message.backtrace.join("\n")].join("\n")
- super(message)
- end
- end
-
- cattr_accessor :pool
-
- def self.setup!(react_js, components_js, replay_console, args={})
- args.assert_valid_keys(:size, :timeout)
- @@react_js = react_js
- @@components_js = components_js
- @@replay_console = replay_console
- @@pool.shutdown{} if @@pool
- reset_combined_js!
- default_pool_options = {:size =>10, :timeout => 20}
- @@pool = ConnectionPool.new(default_pool_options.merge(args)) { self.new }
- end
-
- def self.render(component, args={})
- @@pool.with do |renderer|
- renderer.render(component, args)
- end
- end
-
- def self.react_props(args={})
- if args.is_a? String
- args
- else
- args.to_json
- end
- end
-
- def context
- @context ||= ExecJS.compile(self.class.combined_js)
- end
-
- def render(component, args={})
- react_props = React::Renderer.react_props(args)
- jscode = <<-JS
- (function () {
- var result = React.renderToString(React.createElement(#{component}, #{react_props}));
- #{@@replay_console ? React::Console.replay_as_script_js : ''}
- return result;
- })()
- JS
- context.eval(jscode).html_safe
- rescue ExecJS::ProgramError => e
- raise PrerenderError.new(component, react_props, e)
- end
-
-
- private
-
- def self.setup_combined_js
- <<-JS
- var global = global || this;
- var self = self || this;
- var window = window || this;
- #{React::Console.polyfill_js}
- #{@@react_js.call};
- React = global.React;
- #{@@components_js.call};
- JS
- end
-
- def self.reset_combined_js!
- @@combined_js = setup_combined_js
- end
-
- def self.combined_js
- @@combined_js
- end
-
- end
-end
diff --git a/lib/react/server_rendering.rb b/lib/react/server_rendering.rb
new file mode 100644
index 000000000..050ad4d16
--- /dev/null
+++ b/lib/react/server_rendering.rb
@@ -0,0 +1,24 @@
+require 'connection_pool'
+require 'react/server_rendering/sprockets_renderer'
+
+module React
+ module ServerRendering
+ mattr_accessor :renderer, :renderer_options,
+ :pool_size, :pool_timeout
+
+ def self.reset_pool
+ options = {size: pool_size, timeout: pool_timeout}
+ @@pool = ConnectionPool.new(options) { create_renderer }
+ end
+
+ def self.render(component_name, props)
+ @@pool.with do |renderer|
+ renderer.render(component_name, props)
+ end
+ end
+
+ def self.create_renderer
+ renderer.new(renderer_options)
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/react/server_rendering/sprockets_renderer.rb b/lib/react/server_rendering/sprockets_renderer.rb
new file mode 100644
index 000000000..94682bd43
--- /dev/null
+++ b/lib/react/server_rendering/sprockets_renderer.rb
@@ -0,0 +1,81 @@
+module React
+ module ServerRendering
+ class SprocketsRenderer
+ def initialize(options={})
+ @replay_console = options.fetch(:replay_console, true)
+
+ filenames = options.fetch(:files, ["react.js", "components.js"])
+ js_code = GLOBAL_WRAPPER + CONSOLE_POLYFILL
+
+ filenames.each do |filename|
+ js_code << ::Rails.application.assets[filename].to_s
+ end
+
+ @context = ExecJS.compile(js_code)
+ end
+
+ def render(component_name, props)
+ # pass prerender: :static to use renderToStaticMarkup
+ if props.is_a?(Hash) && props[:prerender] == :static
+ react_render_method = "renderToStaticMarkup"
+ else
+ react_render_method = "renderToString"
+ end
+
+ if !props.is_a?(String)
+ props = props.to_json
+ end
+
+ js_code = <<-JS
+ (function () {
+ var result = React.#{react_render_method}(React.createElement(#{component_name}, #{props}));
+ #{@replay_console ? CONSOLE_REPLAY : ""}
+ return result;
+ })()
+ JS
+
+ @context.eval(js_code).html_safe
+ rescue ExecJS::ProgramError => err
+ raise PrerenderError.new(component_name, props, err)
+ end
+
+ # Handle node.js & other RubyRacer contexts
+ GLOBAL_WRAPPER = <<-JS
+ var global = global || this;
+ var self = self || this;
+ var window = window || this;
+ JS
+
+ # Reimplement console methods for replaying on the client
+ CONSOLE_POLYFILL = <<-JS
+ var console = { history: [] };
+ ['error', 'log', 'info', 'warn'].forEach(function (fn) {
+ console[fn] = function () {
+ console.history.push({level: fn, arguments: Array.prototype.slice.call(arguments)});
+ };
+ });
+ JS
+
+ # Replay message from console history
+ CONSOLE_REPLAY = <<-JS
+ (function (history) {
+ if (history && history.length > 0) {
+ result += '\\n';
+ history.forEach(function (msg) {
+ result += '\\nconsole.' + msg.level + '.apply(console, ' + JSON.stringify(msg.arguments) + ');';
+ });
+ result += '\\n';
+ }
+ })(console.history);
+ JS
+
+ class PrerenderError < RuntimeError
+ def initialize(component_name, props, js_message)
+ message = ["Encountered error \"#{js_message}\" when prerendering #{component_name} with #{props}",
+ js_message.backtrace.join("\n")].join("\n")
+ super(message)
+ end
+ end
+ end
+ end
+end
diff --git a/react-rails.gemspec b/react-rails.gemspec
index 0ea483636..0e98a5cf3 100644
--- a/react-rails.gemspec
+++ b/react-rails.gemspec
@@ -18,6 +18,8 @@ Gem::Specification.new do |s|
s.add_development_dependency 'bundler', '>= 1.2.2'
s.add_development_dependency 'coffee-rails'
s.add_development_dependency 'es5-shim-rails', '>= 2.0.5'
+ s.add_development_dependency 'guard'
+ s.add_development_dependency 'guard-minitest'
s.add_development_dependency 'jbuilder'
s.add_development_dependency 'poltergeist', '>= 0.3.3'
s.add_development_dependency 'test-unit', '~> 2.5'
diff --git a/test/dummy/app/controllers/server_controller.rb b/test/dummy/app/controllers/server_controller.rb
index bc1008375..48f4f9757 100644
--- a/test/dummy/app/controllers/server_controller.rb
+++ b/test/dummy/app/controllers/server_controller.rb
@@ -4,21 +4,14 @@ def show
end
def console_example
- hack_replay_console_config true
+ React::ServerRendering.renderer_options = {replay_console: true}
+ React::ServerRendering.reset_pool
@todos = %w{todo1 todo2 todo3}
end
def console_example_suppressed
- hack_replay_console_config false
+ React::ServerRendering.renderer_options = {replay_console: false}
+ React::ServerRendering.reset_pool
@todos = %w{todo1 todo2 todo3}
end
-
- private
- def hack_replay_console_config(value)
- # Don't do this in your app; just set it how you want it in config/application.rb
- cfg = ::Rails.application.config.react
- cfg.replay_console = value
- React::Renderer.setup!( cfg.react_js, cfg.components_js, cfg.replay_console,
- {:size => cfg.max_renderers, :timeout => cfg.timeout})
- end
end
diff --git a/test/view_helper_test.rb b/test/react/rails/view_helper_test.rb
similarity index 98%
rename from test/view_helper_test.rb
rename to test/react/rails/view_helper_test.rb
index 2160a2304..7624b3c2f 100644
--- a/test/view_helper_test.rb
+++ b/test/react/rails/view_helper_test.rb
@@ -129,7 +129,6 @@ class ViewHelperTest < ActionDispatch::IntegrationTest
test 'react server rendering also gets mounted on client' do
visit '/server/1'
assert_match(/data-react-class=\"TodoList\"/, page.html)
- assert_match(/data-react-checksum/, page.html)
assert_match(/yep/, page.find("#status").text)
end
diff --git a/test/react/server_rendering/sprockets_renderer_test.rb b/test/react/server_rendering/sprockets_renderer_test.rb
new file mode 100644
index 000000000..0b39636b8
--- /dev/null
+++ b/test/react/server_rendering/sprockets_renderer_test.rb
@@ -0,0 +1,57 @@
+require 'test_helper'
+
+class SprocketsRendererTest < ActiveSupport::TestCase
+ setup do
+ @renderer = React::ServerRendering::SprocketsRenderer.new({})
+ end
+
+ test '#render returns HTML' do
+ result = @renderer.render("Todo", {todo: "write tests"})
+ assert_match(/
/, result)
+ assert_match(/data-react-checksum/, result)
+ end
+
+ test '#render accepts strings' do
+ result = @renderer.render("Todo", {todo: "write more tests"}.to_json)
+ assert_match(//, result)
+ end
+
+ test '#render accepts prerender: :static' do
+ result = @renderer.render("Todo", {todo: "write more tests", prerender: :static})
+ assert_match(/write more tests<\/li>/, result)
+ assert_no_match(/data-react-checksum/, result)
+ end
+
+ test '#render replays console messages' do
+ result = @renderer.render("TodoListWithConsoleLog", {todos: ["log some messages"]})
+ assert_match(/console.log.apply\(console, \["got initial state"\]\)/, result)
+ assert_match(/console.warn.apply\(console, \["mounted component"\]\)/, result)
+ assert_match(/console.error.apply\(console, \["rendered!","foo"\]\)/, result)
+ end
+
+ test '#render console messages can be disabled' do
+ no_log_renderer = React::ServerRendering::SprocketsRenderer.new({replay_console: false})
+ result = no_log_renderer.render("TodoListWithConsoleLog", {todos: ["log some messages"]})
+ assert_no_match(/console.log.apply\(console, \["got initial state"\]\)/, result)
+ assert_no_match(/console.warn.apply\(console, \["mounted component"\]\)/, result)
+ assert_no_match(/console.error.apply\(console, \["rendered!","foo"\]\)/, result)
+ end
+
+ test '#render errors include stack traces' do
+ err = assert_raises React::ServerRendering::SprocketsRenderer::PrerenderError do
+ @renderer.render("NonExistentComponent", {})
+ end
+ assert_match(/ReferenceError/, err.to_s)
+ assert_match(/NonExistentComponent/, err.to_s, "it names the component")
+ assert_match(/\n/, err.to_s, "it includes the multi-line backtrace")
+ end
+
+ test '.new accepts any filenames' do
+ limited_renderer = React::ServerRendering::SprocketsRenderer.new(files: ["react.js", "components/Todo.js"])
+ assert_match(/get a real job<\/li>/, limited_renderer.render("Todo", {todo: "get a real job"}))
+ err = assert_raises React::ServerRendering::SprocketsRenderer::PrerenderError do
+ limited_renderer.render("TodoList", {todos: []})
+ end
+ assert_match(/ReferenceError/, err.to_s, "it doesnt load other files")
+ end
+end
\ No newline at end of file
diff --git a/test/react/server_rendering_test.rb b/test/react/server_rendering_test.rb
new file mode 100644
index 000000000..a8a3b1fde
--- /dev/null
+++ b/test/react/server_rendering_test.rb
@@ -0,0 +1,55 @@
+require 'test_helper'
+
+class NullRenderer
+ def initialize(options)
+ # in this case, options is actually a string (just for testing)
+ @name = options
+ end
+
+ def render(component_name, props)
+ "#{@name} rendered #{component_name} with #{props}"
+ end
+end
+
+class ReactServerRenderingTest < ActiveSupport::TestCase
+ setup do
+ @previous_renderer = React::ServerRendering.renderer
+ @previous_options = React::ServerRendering.renderer_options
+ React::ServerRendering.renderer_options = "TEST"
+ React::ServerRendering.renderer = NullRenderer
+ React::ServerRendering.reset_pool
+ end
+
+ teardown do
+ React::ServerRendering.renderer = @previous_renderer
+ React::ServerRendering.renderer_options = @previous_options
+ end
+
+ test '.create_renderer makes a renderer with initialization options' do
+ mock_renderer = Minitest::Mock.new
+ mock_renderer.expect(:new, :fake_renderer, [{mock: true}])
+ React::ServerRendering.renderer = mock_renderer
+ React::ServerRendering.renderer_options = {mock: true}
+ renderer = React::ServerRendering.create_renderer
+ assert_equal(:fake_renderer, renderer)
+ end
+
+ test '.render returns a rendered string' do
+ props = {"props" => true}
+ result = React::ServerRendering.render("MyComponent", props)
+ assert_equal("TEST rendered MyComponent with #{props}", result)
+ end
+
+ test '.reset_pool forgets old renderers' do
+ # At first, they use the first options:
+ assert_match(/^TEST/, React::ServerRendering.render(nil, nil))
+ assert_match(/^TEST/, React::ServerRendering.render(nil, nil))
+
+ # Then change the init options and clear the pool:
+ React::ServerRendering.renderer_options = "DIFFERENT"
+ React::ServerRendering.reset_pool
+ # New renderers are created with the new init options:
+ assert_match(/^DIFFERENT/, React::ServerRendering.render(nil, nil))
+ assert_match(/^DIFFERENT/, React::ServerRendering.render(nil, nil))
+ end
+end
\ No newline at end of file
diff --git a/test/react_renderer_test.rb b/test/react_renderer_test.rb
deleted file mode 100644
index 1508dc9f8..000000000
--- a/test/react_renderer_test.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-require 'test_helper'
-
-class ReactRendererTest < ActiveSupport::TestCase
-
- test 'Server rendering class directly' do
- result = React::Renderer.render "TodoList", :todos => %w{todo1 todo2 todo3}
- assert_match(/todo1.*todo2.*todo3/, result)
- assert_match(/data-react-checksum/, result)
- end
-
- test 'Server rendering with an already-encoded json string' do
- json_string = Jbuilder.new do |json|
- json.todos %w{todo1 todo2 todo3}
- end.target!
-
- result = React::Renderer.render "TodoList", json_string
- assert_match(/todo1.*todo2.*todo3/, result)
- assert_match(/data-react-checksum/, result)
- end
-
- test 'Rendering does not throw an exception when console log api is used' do
- %W(error info log warn).each do |fn|
- assert_nothing_raised(ExecJS::ProgramError) do
- React::Renderer.pool.checkout.context.eval("console.#{fn}()")
- end
- end
- end
-
- test 'prerender errors are thrown' do
- err = assert_raises React::Renderer::PrerenderError do
- React::Renderer.render("NonexistentComponent", {error: true, exists: false})
- end
- expected_message_one = 'Encountered error "ReferenceError: Can\'t find variable: NonexistentComponent" when prerendering NonexistentComponent with {"error":true,"exists":false}'
- expected_message_two = 'Encountered error "ReferenceError: NonexistentComponent is not defined" when prerendering NonexistentComponent with {"error":true,"exists":false}'
- assert (err.message.starts_with?(expected_message_one) || err.message.starts_with?(expected_message_two))
- end
-
- test 'prerender errors include the error backtrace' do
- err = assert_raises React::Renderer::PrerenderError do
- React::Renderer.render("NonexistentComponent", {error: true, exists: false})
- end
-
- assert (err.message.count("\n") > 2), "The error has a multi-line backtrace"
- end
-
- test 'prerender errors are thrown when given a string' do
- json_string = Jbuilder.new do |json|
- json.error true
- json.exists false
- end.target!
-
- err = assert_raises React::Renderer::PrerenderError do
- React::Renderer.render("NonexistentComponent", json_string)
- end
- expected_message_one = 'Encountered error "ReferenceError: Can\'t find variable: NonexistentComponent" when prerendering NonexistentComponent with {"error":true,"exists":false}'
- expected_message_two = 'Encountered error "ReferenceError: NonexistentComponent is not defined" when prerendering NonexistentComponent with {"error":true,"exists":false}'
- assert (err.message.starts_with?(expected_message_one) || err.message.starts_with?(expected_message_two))
- end
-end
diff --git a/test/react_test.rb b/test/react_test.rb
index 1d09c6072..a59ba0762 100644
--- a/test/react_test.rb
+++ b/test/react_test.rb
@@ -1,20 +1,35 @@
require 'test_helper'
-require 'fileutils'
-
class ReactTest < ActionDispatch::IntegrationTest
+ setup do
+ FileUtils.rm_r(CACHE_PATH) if CACHE_PATH.exist?
+
+ end
+
+ teardown do
+ FileUtils.rm_r(CACHE_PATH) if CACHE_PATH.exist?
+ end
+
test 'asset pipeline should deliver drop-in react file replacement' do
app_react_file_path = File.expand_path("../dummy/vendor/assets/javascripts/react.js", __FILE__)
-
- react_file_token = "'test_confirmation_token_react_content_non_production';\n";
+ react_file_token = "'test_confirmation_token_react_content_non_production';\n"
File.write(app_react_file_path, react_file_token)
+ react_asset = Rails.application.assets['react.js']
+
+ # Sprockets 2 doesn't expire this asset correctly,
+ # so override `fresh?` to mark it as expired.
+ def react_asset.fresh?(env); false; end
+
+ react_asset = Rails.application.assets['react.js']
+
get '/assets/react.js'
File.unlink(app_react_file_path)
- FileUtils.rm_r CACHE_PATH if CACHE_PATH.exist?
+ FileUtils.rm_r(CACHE_PATH) if CACHE_PATH.exist?
assert_response :success
- assert_equal react_file_token, @response.body
+ assert_equal react_file_token.length, react_asset.to_s.length, "The asset pipeline serves the drop-in file"
+ assert_equal react_file_token.length, @response.body.length, "The asset route serves the drop-in file"
end
test 'precompiling assets works' do
diff --git a/test/server_rendered_html_test.rb b/test/server_rendered_html_test.rb
index 8b9ceba83..09fb7eb5c 100644
--- a/test/server_rendered_html_test.rb
+++ b/test/server_rendered_html_test.rb
@@ -46,7 +46,7 @@ def wait_to_ensure_asset_pipeline_detects_changes
# Make sure they're not when we don't ask for them
get '/server/console_example_suppressed'
- assert_match('Console Logged', response.body)
+ assert_match(/Console Logged/, response.body)
assert_no_match(/console.log/, response.body)
assert_no_match(/console.warn/, response.body)
assert_no_match(/console.error/, response.body)
diff --git a/test/test_helper.rb b/test/test_helper.rb
index a65a1b5b7..72b61e4a3 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -5,6 +5,7 @@
require "rails/test_help"
require "rails/generators"
require "pathname"
+require 'minitest/mock'
CACHE_PATH = Pathname.new File.expand_path("../dummy/tmp/cache", __FILE__)