hyper-spec 1.0.alpha1.8.1 → 1.0.0.lap28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/hyper-spec.gemspec CHANGED
@@ -6,49 +6,53 @@ require 'hyper-spec/version'
6
6
  Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
7
7
  spec.name = 'hyper-spec'
8
8
  spec.version = HyperSpec::VERSION
9
- spec.authors = ['Mitch VanDuyn', 'AdamCreekroad', 'Jan Biedermann']
9
+ spec.authors = ['Mitch VanDuyn', 'Adam Creekroad', 'Jan Biedermann']
10
10
  spec.email = ['mitch@catprint.com', 'jan@kursator.com']
11
- spec.summary = 'Drive your Opal and Hyperstack client and server specs from RSpec and Capybara'
12
- spec.description = 'A Hyperstack application consists of isomorphic React Components, '\
11
+ spec.summary = 'Drive your Hyperloop client and server specs from RSpec and Capybara'
12
+ spec.description = 'A Hyperloop application consists of isomorphic React Components, '\
13
13
  'Active Record Models, Stores, Operations and Policiespec. '\
14
14
  'Test them all from Rspec, regardless if the code runs on the client or server.'
15
- spec.homepage = 'http://hyperstack.org'
15
+ spec.homepage = 'http://ruby-hyperloop.org'
16
16
  spec.license = 'MIT'
17
+ # spec.metadata = {
18
+ # "homepage_uri" => 'http://ruby-hyperloop.org',
19
+ # "source_code_uri" => 'https://github.com/ruby-hyperloop/hyper-component'
20
+ # }
21
+
17
22
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(gemfiles|spec)/}) }
18
23
  spec.bindir = 'exe'
19
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
25
  spec.require_paths = ['lib']
21
26
 
22
- spec.add_dependency 'actionview'
23
27
  spec.add_dependency 'capybara'
24
- spec.add_dependency 'filecache'
28
+ spec.add_dependency 'chromedriver-helper'
25
29
  spec.add_dependency 'method_source'
26
- spec.add_dependency 'opal', ENV['OPAL_VERSION'] || '>= 0.11.0', '< 2.0'
27
- spec.add_dependency 'parser'
28
- spec.add_dependency 'rspec'
30
+ spec.add_dependency 'opal', '>= 0.11.0', '< 0.12.0'
31
+ spec.add_dependency 'parser', '>= 2.3.3.1'
32
+ spec.add_dependency 'pry'
33
+ spec.add_dependency 'rspec-rails'
29
34
  spec.add_dependency 'selenium-webdriver'
30
35
  spec.add_dependency 'timecop', '~> 0.8.1'
31
36
  spec.add_dependency 'uglifier'
32
- spec.add_dependency 'unparser', '>= 0.4.2'
37
+ spec.add_dependency 'unparser'
38
+ spec.add_dependency 'webdrivers'
33
39
 
34
40
  spec.add_development_dependency 'bundler'
35
- spec.add_development_dependency 'hyper-component', HyperSpec::VERSION
36
- spec.add_development_dependency 'mini_racer'
41
+ spec.add_development_dependency 'hyper-react', HyperSpec::VERSION
42
+ spec.add_development_dependency 'mini_racer', '~> 0.1.15'
37
43
  spec.add_development_dependency 'opal-browser', '~> 0.2.0'
38
- spec.add_development_dependency 'opal-rails', '>= 0.9.4'
39
- spec.add_development_dependency 'pry-rescue'
40
- spec.add_development_dependency 'pry-stack_explorer'
44
+ spec.add_development_dependency 'opal-rails', '~> 0.9.4'
41
45
  spec.add_development_dependency 'puma'
42
- spec.add_development_dependency 'rails', ENV['RAILS_VERSION'] || '>= 5.0.0', '< 7.0'
46
+ spec.add_development_dependency 'rails', '>= 4.0.0'
43
47
  spec.add_development_dependency 'rake'
48
+ spec.add_development_dependency 'pry-rescue'
44
49
  spec.add_development_dependency 'react-rails', '>= 2.3.0', '< 2.5.0'
45
- spec.add_development_dependency 'rspec-rails'
46
50
  spec.add_development_dependency 'rspec-collection_matchers'
47
51
  spec.add_development_dependency 'rspec-expectations'
48
52
  spec.add_development_dependency 'rspec-its'
49
53
  spec.add_development_dependency 'rspec-mocks'
50
54
  spec.add_development_dependency 'rspec-steps', '~> 2.1.1'
51
- spec.add_development_dependency 'rubocop' #, '~> 0.51.0'
55
+ spec.add_development_dependency 'rubocop', '~> 0.51.0'
52
56
  spec.add_development_dependency 'shoulda'
53
57
  spec.add_development_dependency 'shoulda-matchers'
54
58
  spec.add_development_dependency 'spring-commands-rspec'
@@ -0,0 +1,360 @@
1
+ # see component_test_helpers_spec.rb for examples
2
+ require 'parser/current'
3
+ require 'unparser'
4
+ require 'method_source'
5
+ require_relative '../../lib/hyper-spec/time_cop.rb'
6
+
7
+ module HyperSpec
8
+ module ComponentTestHelpers
9
+ TOP_LEVEL_COMPONENT_PATCH =
10
+ Opal.compile(File.read(File.expand_path('../../react/top_level_rails_component.rb', __FILE__)))
11
+ TIME_COP_CLIENT_PATCH =
12
+ Opal.compile(File.read(File.expand_path('../../hyper-spec/time_cop.rb', __FILE__))) +
13
+ "\n#{File.read(File.expand_path('../../sources/lolex.js', __FILE__))}"
14
+
15
+ class << self
16
+ attr_accessor :current_example
17
+ attr_accessor :description_displayed
18
+
19
+ def display_example_description
20
+ "<script type='text/javascript'>console.log('%c#{current_example.description}'"\
21
+ ",'color:green; font-weight:bold; font-size: 200%')</script>"
22
+ end
23
+ end
24
+
25
+ def build_test_url_for(controller)
26
+ unless controller
27
+ unless defined?(::ReactTestController)
28
+ Object.const_set('ReactTestController', Class.new(::ActionController::Base))
29
+ end
30
+
31
+ controller = ::ReactTestController
32
+ end
33
+
34
+ route_root = controller.name.gsub(/Controller$/, '').underscore
35
+
36
+ unless controller.method_defined?(:test)
37
+ controller.class_eval do
38
+ define_method(:test) do
39
+ route_root = self.class.name.gsub(/Controller$/, '').underscore
40
+ test_params = ::Rails.cache.read("/#{route_root}/#{params[:id]}")
41
+ @component_name = test_params[0]
42
+ @component_params = test_params[1]
43
+ render_params = test_params[2]
44
+ render_on = render_params.delete(:render_on) || :client_only
45
+ _mock_time = render_params.delete(:mock_time)
46
+ style_sheet = render_params.delete(:style_sheet)
47
+ javascript = render_params.delete(:javascript)
48
+ code = render_params.delete(:code)
49
+
50
+ page = '<%= react_component @component_name, @component_params, '\
51
+ "{ prerender: #{render_on != :client_only} } %>"
52
+ unless render_on == :server_only
53
+ page = "<script type='text/javascript'>\n#{TOP_LEVEL_COMPONENT_PATCH}\n</script>\n#{page}"
54
+ page = "<script type='text/javascript'>\n#{code}\n</script>\n#{page}" if code
55
+ end
56
+
57
+ if render_on != :server_only || Lolex.initialized?
58
+ page = "<script type='text/javascript'>\n#{TIME_COP_CLIENT_PATCH}\n</script>\n#{page}"
59
+ end
60
+
61
+ if (render_on != :server_only && !render_params[:layout]) || javascript
62
+ page = "<%= javascript_include_tag '#{javascript || 'application'}' %>\n#{page}"
63
+ end
64
+
65
+ if !render_params[:layout] || style_sheet
66
+ page = "<%= stylesheet_link_tag '#{style_sheet || 'application'}' %>\n#{page}"
67
+ end
68
+ page = "<script type='text/javascript'>go = function() "\
69
+ "{window.hyper_spec_waiting_for_go = false}</script>\n#{page}"
70
+
71
+ title = view_context.escape_javascript(ComponentTestHelpers.current_example.description)
72
+ title = "#{title}...continued." if ComponentTestHelpers.description_displayed
73
+
74
+ page = "<script type='text/javascript'>console.log('%c#{title}',"\
75
+ "'color:green; font-weight:bold; font-size: 200%')</script>\n#{page}"
76
+
77
+ ComponentTestHelpers.description_displayed = true
78
+ render_params[:inline] = page
79
+ render render_params
80
+ end
81
+ end
82
+
83
+ begin
84
+ routes = ::Rails.application.routes
85
+ routes.disable_clear_and_finalize = true
86
+ routes.clear!
87
+ routes.draw do
88
+ get "/#{route_root}/:id", to: "#{route_root}#test"
89
+ end
90
+ ::Rails.application.routes_reloader.paths.each { |path| load(path) }
91
+ routes.finalize!
92
+ ActiveSupport.on_load(:action_controller) { routes.finalize! }
93
+ ensure
94
+ routes.disable_clear_and_finalize = false
95
+ end
96
+ end
97
+
98
+ "/#{route_root}/#{@test_id = (@test_id || 0) + 1}"
99
+ end
100
+
101
+ def isomorphic(&block)
102
+ yield
103
+ on_client(&block)
104
+ end
105
+
106
+ def evaluate_ruby(str = '', opts = {}, &block)
107
+ insure_mount
108
+ if block
109
+ str = "#{str}\n#{Unparser.unparse Parser::CurrentRuby.parse(block.source).children.last}"
110
+ end
111
+ js = Opal.compile(str).delete("\n").gsub('(Opal);', '(Opal)')
112
+ # workaround for firefox 58 and geckodriver 0.19.1, because firefox is unable to find .$to_json:
113
+ # JSON.parse(evaluate_script("(function(){var a=Opal.Array.$new(); a[0]=#{js}; return a.$to_json();})();"), opts).first
114
+ JSON.parse(evaluate_script("[#{js}].$to_json()"), opts).first
115
+ end
116
+
117
+ def expect_evaluate_ruby(str = '', opts = {}, &block)
118
+ insure_mount
119
+ expect(evaluate_ruby(add_opal_block(str, block), opts))
120
+ end
121
+
122
+ def add_opal_block(str, block)
123
+ # big assumption here is that we are going to follow this with a .to
124
+ # hence .children.first followed by .children.last
125
+ # probably should do some kind of "search" to make this work nicely
126
+ return str unless block
127
+ "#{str}\n"\
128
+ "#{Unparser.unparse Parser::CurrentRuby.parse(block.source).children.first.children.last}"
129
+ end
130
+
131
+ def evaluate_promise(str = '', opts = {}, &block)
132
+ insure_mount
133
+ str = "#{str}\n#{Unparser.unparse Parser::CurrentRuby.parse(block.source).children.last}" if block
134
+ str = "#{str}.then { |args| args = [args]; `window.hyper_spec_promise_result = args` }"
135
+ js = Opal.compile(str).gsub("\n","").gsub("(Opal);","(Opal)")
136
+ page.evaluate_script("window.hyper_spec_promise_result = false")
137
+ page.execute_script(js)
138
+ Timeout.timeout(Capybara.default_max_wait_time) do
139
+ loop do
140
+ sleep 0.25
141
+ break if page.evaluate_script("!!window.hyper_spec_promise_result")
142
+ end
143
+ end
144
+ JSON.parse(page.evaluate_script("window.hyper_spec_promise_result.$to_json()"), opts).first
145
+ end
146
+
147
+ def expect_promise(str = '', opts = {}, &block)
148
+ insure_mount
149
+ expect(evaluate_promise(add_opal_block(str, block), opts))
150
+ end
151
+
152
+ def ppr(str)
153
+ js = Opal.compile(str).delete("\n").gsub('(Opal);', '(Opal)')
154
+ execute_script("console.log(#{js})")
155
+ end
156
+
157
+ def on_client(&block)
158
+ @client_code =
159
+ "#{@client_code}#{Unparser.unparse Parser::CurrentRuby.parse(block.source).children.last}\n"
160
+ end
161
+
162
+ def debugger
163
+ `debugger`
164
+ nil
165
+ end
166
+
167
+ def insure_mount
168
+ # rescue in case page is not defined...
169
+ mount unless page.instance_variable_get('@hyper_spec_mounted')
170
+ end
171
+
172
+ def client_option(opts = {})
173
+ @client_options ||= {}
174
+ @client_options.merge! opts
175
+ end
176
+
177
+ alias client_options client_option
178
+
179
+ def mount(component_name = nil, params = nil, opts = {}, &block)
180
+ unless params
181
+ params = opts
182
+ opts = {}
183
+ end
184
+
185
+ opts = client_options opts
186
+ test_url = build_test_url_for(opts.delete(:controller))
187
+
188
+ if block || @client_code || component_name.nil?
189
+ block_with_helpers = <<-code
190
+ module ComponentHelpers
191
+ def self.js_eval(s)
192
+ `eval(s)`
193
+ end
194
+ def self.dasherize(s)
195
+ res = %x{
196
+ s.replace(/[-_\\s]+/g, '-')
197
+ .replace(/([A-Z\\d]+)([A-Z][a-z])/g, '$1-$2')
198
+ .replace(/([a-z\\d])([A-Z])/g, '$1-$2')
199
+ .toLowerCase()
200
+ }
201
+ res
202
+ end
203
+ def self.add_class(class_name, styles={})
204
+ style = styles.collect { |attr, value| "\#{dasherize(attr)}:\#{value}" }.join("; ")
205
+ cs = class_name.to_s
206
+ %x{
207
+ var style_el = document.createElement("style");
208
+ var css = "." + cs + " { " + style + " }";
209
+ style_el.type = "text/css";
210
+ if (style_el.styleSheet){
211
+ style_el.styleSheet.cssText = css;
212
+ } else {
213
+ style_el.appendChild(document.createTextNode(css));
214
+ }
215
+ document.head.appendChild(style_el);
216
+ }
217
+ end
218
+ end
219
+ class React::Component::HyperTestDummy < React::Component::Base
220
+ def render; end
221
+ end
222
+ #{@client_code}
223
+ #{Unparser.unparse(Parser::CurrentRuby.parse(block.source).children.last) if block}
224
+ code
225
+ opts[:code] = Opal.compile(block_with_helpers)
226
+ end
227
+
228
+ component_name ||= 'React::Component::HyperTestDummy'
229
+ ::Rails.cache.write(test_url, [component_name, params, opts])
230
+ test_code_key = "hyper_spec_prerender_test_code.js"
231
+ @@original_server_render_files ||= ::Rails.configuration.react.server_renderer_options[:files]
232
+ if opts[:render_on] == :both || opts[:render_on] == :server_only
233
+ unless opts[:code].blank?
234
+ ::Rails.cache.write(test_code_key, opts[:code])
235
+ ::Rails.configuration.react.server_renderer_options[:files] = @@original_server_render_files + [test_code_key]
236
+ ::React::ServerRendering.reset_pool # make sure contexts are reloaded so they dont use code from cache, as the rails filewatcher doesnt look for cache changes
237
+ else
238
+ ::Rails.cache.delete(test_code_key)
239
+ ::Rails.configuration.react.server_renderer_options[:files] = @@original_server_render_files
240
+ ::React::ServerRendering.reset_pool # make sure contexts are reloaded so they dont use code from cache, as the rails filewatcher doesnt look for cache changes
241
+ end
242
+ end
243
+ Lolex.init(self, client_options[:time_zone], client_options[:clock_resolution])
244
+ visit test_url
245
+ wait_for_ajax unless opts[:no_wait]
246
+ page.instance_variable_set('@hyper_spec_mounted', true)
247
+ end
248
+
249
+ [:callback_history_for, :last_callback_for, :clear_callback_history_for,
250
+ :event_history_for, :last_event_for, :clear_event_history_for].each do |method|
251
+ define_method(method) do |event_name|
252
+ evaluate_ruby("React::TopLevelRailsComponent.#{method}('#{event_name}')")
253
+ end
254
+ end
255
+
256
+ def run_on_client(&block)
257
+ script = Opal.compile(Unparser.unparse(Parser::CurrentRuby.parse(block.source).children.last))
258
+ execute_script(script)
259
+ end
260
+
261
+ def add_class(class_name, style)
262
+ @client_code = "#{@client_code}ComponentHelpers.add_class('#{class_name}', #{style})\n"
263
+ end
264
+
265
+ def open_in_chrome
266
+ if false && ['linux', 'freebsd'].include?(`uname`.downcase)
267
+ `google-chrome http://#{page.server.host}:#{page.server.port}#{page.current_path}`
268
+ else
269
+ `open http://#{page.server.host}:#{page.server.port}#{page.current_path}`
270
+ end
271
+
272
+ while true
273
+ sleep 1.hour
274
+ end
275
+ end
276
+
277
+ def pause(message = nil)
278
+ if message
279
+ puts message
280
+ page.evaluate_ruby "puts #{message.inspect}.to_s + ' (type go() to continue)'"
281
+ end
282
+
283
+ page.evaluate_script('window.hyper_spec_waiting_for_go = true')
284
+
285
+ loop do
286
+ sleep 0.25
287
+ break unless page.evaluate_script('window.hyper_spec_waiting_for_go')
288
+ end
289
+ end
290
+
291
+ def wait_for_size(width, height)
292
+ start_time = Capybara::Helpers.monotonic_time
293
+ stable_count_w = 0
294
+ stable_count_h = 0
295
+ prev_size = [0, 0]
296
+ begin
297
+ sleep 0.05
298
+ curr_size = Capybara.current_session.current_window.size
299
+ return if [width, height] == curr_size
300
+ # some maximum or minimum is reached and size doesnt change anymore
301
+ stable_count_w += 1 if prev_size[0] == curr_size[0]
302
+ stable_count_h += 1 if prev_size[1] == curr_size[1]
303
+ return if stable_count_w > 2 || stable_count_h > 2
304
+ prev_size = curr_size
305
+ end while (Capybara::Helpers.monotonic_time - start_time) < Capybara.current_session.config.default_max_wait_time
306
+ raise Capybara::WindowError, "Window size not stable within #{Capybara.current_session.config.default_max_wait_time} seconds."
307
+ end
308
+
309
+ def size_window(width = nil, height = nil)
310
+ # return if @window_cannot_be_resized
311
+ # original_width = evaluate_script('window.innerWidth')
312
+ # original_height = evaluate_script('window.innerHeight')
313
+ width, height = [height, width] if width == :portrait
314
+ width, height = width if width.is_a? Array
315
+ portrait = true if height == :portrait
316
+
317
+ case width
318
+ when :small
319
+ width, height = [480, 320]
320
+ when :mobile
321
+ width, height = [640, 480]
322
+ when :tablet
323
+ width, height = [960, 640]
324
+ when :large
325
+ width, height = [1920, 6000]
326
+ when :default, nil
327
+ width, height = [1024, 768]
328
+ end
329
+
330
+ width, height = [height, width] if portrait
331
+
332
+ unless RSpec.configuration.debugger_width
333
+ Capybara.current_session.current_window.resize_to(1000, 500)
334
+ wait_for_size(1000, 500)
335
+ inner_width = evaluate_script('window.innerWidth')
336
+ RSpec.configuration.debugger_width = 1000 - inner_width
337
+ end
338
+ Capybara.current_session.current_window
339
+ .resize_to(width + RSpec.configuration.debugger_width, height)
340
+ wait_for_size(width + RSpec.configuration.debugger_width, height)
341
+ end
342
+ end
343
+
344
+ RSpec.configure do |config|
345
+ config.before(:each) do |example|
346
+ ComponentTestHelpers.current_example = example
347
+ ComponentTestHelpers.description_displayed = false
348
+ end
349
+
350
+ if defined?(ActiveRecord)
351
+ config.before(:all) do
352
+ ActiveRecord::Base.class_eval do
353
+ def attributes_on_client(page)
354
+ page.evaluate_ruby("#{self.class.name}.find(#{id}).attributes", symbolize_names: true)
355
+ end
356
+ end
357
+ end
358
+ end
359
+ end
360
+ end
@@ -59,10 +59,6 @@ if RUBY_ENGINE == 'opal'
59
59
  ticker
60
60
  end
61
61
 
62
- def init(scale: 1, resolution: 10)
63
- update_lolex(Time.now, scale, resolution)
64
- end
65
-
66
62
  def update_lolex(time, scale, resolution)
67
63
  `#{@lolex}.uninstall()` && return if scale.nil?
68
64
  @mock_start_time = time.to_f * 1000
@@ -84,14 +80,6 @@ if RUBY_ENGINE == 'opal'
84
80
  end
85
81
  end
86
82
 
87
- # create an alias for Lolex.init so we can say Timecop.init on the client
88
-
89
- class Timecop
90
- def self.init(*args)
91
- Lolex.init(*args)
92
- end
93
- end
94
-
95
83
  else
96
84
  require 'timecop'
97
85
 
@@ -148,7 +136,7 @@ else
148
136
 
149
137
  def evaluate_ruby(&block)
150
138
  if @capybara_page
151
- @capybara_page.internal_evaluate_ruby(yield)
139
+ @capybara_page.evaluate_ruby(yield)
152
140
  else
153
141
  pending_evaluations << block
154
142
  end
@@ -156,7 +144,7 @@ else
156
144
 
157
145
  def run_pending_evaluations
158
146
  return if pending_evaluations.empty?
159
- @capybara_page.internal_evaluate_ruby(pending_evaluations.collect(&:call).join("\n"))
147
+ @capybara_page.evaluate_ruby(pending_evaluations.collect(&:call).join("\n"))
160
148
  @pending_evaluations ||= []
161
149
  end
162
150
  end
@@ -1,3 +1,3 @@
1
1
  module HyperSpec
2
- VERSION = '1.0.alpha1.8.1'
2
+ VERSION = '1.0.0.lap28'
3
3
  end
@@ -12,9 +12,9 @@ module HyperSpec
12
12
  def running?
13
13
  jscode = <<-CODE
14
14
  (function() {
15
- if (typeof Opal !== "undefined" && Opal.Hyperstack !== undefined) {
15
+ if (typeof Opal !== "undefined" && Opal.Hyperloop !== undefined) {
16
16
  try {
17
- return Opal.Hyperstack.$const_get("HTTP")["$active?"]();
17
+ return Opal.Hyperloop.$const_get("HTTP")["$active?"]();
18
18
  } catch(err) {
19
19
  if (typeof jQuery !== "undefined" && jQuery.active !== undefined) {
20
20
  return (jQuery.active > 0);
@@ -29,7 +29,7 @@ module HyperSpec
29
29
  }
30
30
  })();
31
31
  CODE
32
- Capybara.page.evaluate_script(jscode)
32
+ page.evaluate_script(jscode)
33
33
  rescue Exception => e
34
34
  puts "wait_for_ajax failed while testing state of ajax requests: #{e}"
35
35
  end
@@ -39,7 +39,7 @@ module HyperSpec
39
39
  rescue Capybara::NotSupportedByDriverError
40
40
  true
41
41
  rescue Exception => e
42
- e.message == 'either jQuery or Hyperstack::HTTP is not defined'
42
+ e.message == 'either jQuery or Hyperloop::HTTP is not defined'
43
43
  end
44
44
  end
45
45
  end