hyper-spec 1.0.alpha1.5 → 1.0.alpha1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,70 @@
1
+ module HyperSpec
2
+ module Internal
3
+ module Controller
4
+ class << self
5
+ attr_accessor :current_example
6
+ attr_accessor :description_displayed
7
+
8
+ def test_id
9
+ @_hyperspec_private_test_id ||= 0
10
+ @_hyperspec_private_test_id += 1
11
+ end
12
+
13
+ include ActionView::Helpers::JavaScriptHelper
14
+
15
+ def current_example_description!
16
+ title = "#{title}...continued." if description_displayed
17
+ self.description_displayed = true
18
+ "#{escape_javascript(current_example.description)}#{title}"
19
+ end
20
+
21
+ def file_cache
22
+ @file_cache ||= FileCache.new('cache', '/tmp/hyper-spec-caches', 30, 3)
23
+ end
24
+
25
+ def cache_read(key)
26
+ file_cache.get(key)
27
+ end
28
+
29
+ def cache_write(key, value)
30
+ file_cache.set(key, value)
31
+ end
32
+
33
+ def cache_delete(key)
34
+ file_cache.delete(key)
35
+ rescue StandardError
36
+ nil
37
+ end
38
+ end
39
+
40
+ # By default we assume we are operating in a Rails environment and will
41
+ # hook in using a rails controller. To override this define the
42
+ # HyperSpecController class in your spec helper. See the rack.rb file
43
+ # for an example of how to do this.
44
+
45
+ def hyper_spec_test_controller
46
+ return ::HyperSpecTestController if defined?(::HyperSpecTestController)
47
+
48
+ base = if defined? ApplicationController
49
+ Class.new ApplicationController
50
+ elsif defined? ::ActionController::Base
51
+ Class.new ::ActionController::Base
52
+ else
53
+ raise "Unless using Rails you must define the HyperSpecTestController\n"\
54
+ 'For rack apps try requiring hyper-spec/rack.'
55
+ end
56
+ Object.const_set('HyperSpecTestController', base)
57
+ end
58
+
59
+ # First insure we have a controller, then make sure it responds to the test method
60
+ # if not, then add the rails specific controller methods. The RailsControllerHelpers
61
+ # module will automatically add a top level route back to the controller.
62
+
63
+ def route_root_for(controller)
64
+ controller ||= hyper_spec_test_controller
65
+ controller.include RailsControllerHelpers unless controller.method_defined?(:test)
66
+ controller.route_root
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,103 @@
1
+ module HyperSpec
2
+ module Internal
3
+ module CopyLocals
4
+ private
5
+
6
+ def build_var_inclusion_lists
7
+ build_included_list
8
+ build_excluded_list
9
+ end
10
+
11
+ def build_included_list
12
+ @_hyperspec_private_included_vars = nil
13
+ return unless @_hyperspec_private_client_options.key? :include_vars
14
+
15
+ included = @_hyperspec_private_client_options[:include_vars]
16
+ if included.is_a? Symbol
17
+ @_hyperspec_private_included_vars = [included]
18
+ elsif included.is_a?(Array)
19
+ @_hyperspec_private_included_vars = included
20
+ elsif !included
21
+ @_hyperspec_private_included_vars = []
22
+ end
23
+ end
24
+
25
+ PRIVATE_VARIABLES = %i[
26
+ @__inspect_output @__memoized @example @_hyperspec_private_client_code
27
+ @_hyperspec_private_html_block @fixture_cache
28
+ @fixture_connections @connection_subscriber @loaded_fixtures
29
+ @_hyperspec_private_client_options
30
+ @_hyperspec_private_included_vars
31
+ @_hyperspec_private_excluded_vars
32
+ b __ _ _ex_ pry_instance _out_ _in_ _dir_ _file_
33
+ ]
34
+
35
+ def build_excluded_list
36
+ return unless @_hyperspec_private_client_options
37
+
38
+ excluded = @_hyperspec_private_client_options[:exclude_vars]
39
+ if excluded.is_a? Symbol
40
+ @_hyperspec_private_excluded_vars = [excluded]
41
+ elsif excluded.is_a?(Array)
42
+ @_hyperspec_private_excluded_vars = excluded
43
+ elsif excluded
44
+ @_hyperspec_private_included_vars = []
45
+ end
46
+ end
47
+
48
+ def var_excluded?(var, binding)
49
+ return true if PRIVATE_VARIABLES.include? var
50
+
51
+ excluded = binding.eval('instance_variable_get(:@_hyperspec_private_excluded_vars)')
52
+ return true if excluded&.include?(var)
53
+
54
+ included = binding.eval('instance_variable_get(:@_hyperspec_private_included_vars)')
55
+ included && !included.include?(var)
56
+ end
57
+
58
+ def add_locals(in_str, block)
59
+ b = block.binding
60
+ add_instance_vars(b, add_local_vars(b, add_memoized_vars(b, in_str)))
61
+ end
62
+
63
+ def add_memoized_vars(binding, in_str)
64
+ memoized = binding.eval('__memoized').instance_variable_get(:@memoized)
65
+ return in_str unless memoized
66
+
67
+ memoized.inject(in_str) do |str, pair|
68
+ next str if var_excluded?(pair.first, binding)
69
+
70
+ "#{str}\n#{set_local_var(pair.first, pair.last)}"
71
+ end
72
+ end
73
+
74
+ def add_local_vars(binding, in_str)
75
+ binding.local_variables.inject(in_str) do |str, var|
76
+ next str if var_excluded?(var, binding)
77
+
78
+ "#{str}\n#{set_local_var(var, binding.local_variable_get(var))}"
79
+ end
80
+ end
81
+
82
+ def add_instance_vars(binding, in_str)
83
+ binding.eval('instance_variables').inject(in_str) do |str, var|
84
+ next str if var_excluded?(var, binding)
85
+
86
+ "#{str}\n#{set_local_var(var, binding.eval("instance_variable_get('#{var}')"))}"
87
+ end
88
+ end
89
+
90
+ def set_local_var(name, object)
91
+ serialized = object.opal_serialize
92
+ if serialized
93
+ "#{name} = #{serialized}"
94
+ else
95
+ "self.class.define_method(:#{name}) "\
96
+ "{ raise 'Attempt to access the variable #{name} "\
97
+ 'that was defined in the spec, but its value could not be serialized '\
98
+ "so it is undefined on the client.' }"
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,86 @@
1
+ module Opal
2
+ # strips off stuff that confuses things when transmitting to the client
3
+ # and prints offending code if it can't be compiled
4
+ def self.hyperspec_compile(str, opts = {})
5
+ compile(str, opts).gsub("// Prepare super implicit arguments\n", '')
6
+ .delete("\n").gsub('(Opal);', '(Opal)')
7
+ # rubocop:disable Lint/RescueException
8
+ # we are going to reraise it anyway, so its fine to catch EVERYTHING!
9
+ rescue Exception => e
10
+ puts "puts could not compile: \n\n#{str}\n\n"
11
+ raise e
12
+ end
13
+ # rubocop:enable Lint/RescueException
14
+ end
15
+
16
+ module Unparser
17
+ class Emitter
18
+ # Emitter for send
19
+ class Send < self
20
+ def local_variable_clash?
21
+ selector =~ /^[A-Z]/ ||
22
+ local_variable_scope.local_variable_defined_for_node?(node, selector)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ module MethodSource
29
+ class << self
30
+ alias original_lines_for_before_hyper_spec lines_for
31
+ alias original_source_helper_before_hyper_spec source_helper
32
+
33
+ def source_helper(source_location, name = nil)
34
+ source_location[1] = 1 if source_location[0] == '(pry)'
35
+ original_source_helper_before_hyper_spec source_location, name
36
+ end
37
+
38
+ def lines_for(file_name, name = nil)
39
+ if file_name == '(pry)'
40
+ HyperSpec.current_pry_code_block
41
+ else
42
+ original_lines_for_before_hyper_spec file_name, name
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ class Object
49
+ def opal_serialize
50
+ nil
51
+ end
52
+ end
53
+
54
+ class Hash
55
+ def opal_serialize
56
+ "{#{collect { |k, v| "#{k.opal_serialize} => #{v.opal_serialize}" }.join(', ')}}"
57
+ end
58
+ end
59
+
60
+ class Array
61
+ def opal_serialize
62
+ "[#{collect { |v| v.opal_serialize }.join(', ')}]"
63
+ end
64
+ end
65
+
66
+ [FalseClass, Float, Integer, NilClass, String, Symbol, TrueClass].each do |klass|
67
+ klass.send(:define_method, :opal_serialize) do
68
+ inspect
69
+ end
70
+ end
71
+
72
+ # rubocop:disable Lint/UnifiedInteger - patch for ruby prior to 2.4
73
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.4.0')
74
+ [Bignum, Fixnum].each do |klass|
75
+ klass.send(:define_method, :opal_serialize) do
76
+ inspect
77
+ end
78
+ end
79
+ end
80
+ # rubocop:enable Lint/UnifiedInteger
81
+
82
+ class Time
83
+ def to_opal_expression
84
+ "Time.parse('#{inspect}')"
85
+ end
86
+ end
@@ -0,0 +1,50 @@
1
+ module HyperSpec
2
+ module Internal
3
+ module RailsControllerHelpers
4
+ def self.included(base)
5
+ base.include ControllerHelpers
6
+ base.include Helpers
7
+ routes = ::Rails.application.routes
8
+ routes.disable_clear_and_finalize = true
9
+ routes.clear!
10
+ routes.draw { get "/#{base.route_root}/:id", to: "#{base.route_root}#test" }
11
+ ::Rails.application.routes_reloader.paths.each { |path| load(path) }
12
+ routes.finalize!
13
+ ActiveSupport.on_load(:action_controller) { routes.finalize! }
14
+ ensure
15
+ routes.disable_clear_and_finalize = false
16
+ end
17
+
18
+ module Helpers
19
+ def ping!
20
+ head(:no_content)
21
+ nil
22
+ end
23
+
24
+ def mount_component!
25
+ @page << '<%= react_component @component_name, @component_params, '\
26
+ "{ prerender: #{@render_on != :client_only} } %>"
27
+ end
28
+
29
+ def application!(file)
30
+ @page << "<%= javascript_include_tag '#{file}' %>"
31
+ end
32
+
33
+ def style_sheet!(file)
34
+ @page << "<%= stylesheet_link_tag '#{file}' %>"
35
+ end
36
+
37
+ def deliver!
38
+ @render_params[:inline] = @page
39
+ response.headers['Cache-Control'] = 'max-age=120'
40
+ response.headers['X-Tracking-ID'] = '123456'
41
+ render @render_params
42
+ end
43
+
44
+ def server_only?
45
+ @render_on == :server_only
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -59,6 +59,10 @@ 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
+
62
66
  def update_lolex(time, scale, resolution)
63
67
  `#{@lolex}.uninstall()` && return if scale.nil?
64
68
  @mock_start_time = time.to_f * 1000
@@ -80,6 +84,14 @@ if RUBY_ENGINE == 'opal'
80
84
  end
81
85
  end
82
86
 
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
+
83
95
  else
84
96
  require 'timecop'
85
97
 
@@ -136,7 +148,7 @@ else
136
148
 
137
149
  def evaluate_ruby(&block)
138
150
  if @capybara_page
139
- @capybara_page.evaluate_ruby(yield)
151
+ @capybara_page.internal_evaluate_ruby(yield)
140
152
  else
141
153
  pending_evaluations << block
142
154
  end
@@ -144,7 +156,7 @@ else
144
156
 
145
157
  def run_pending_evaluations
146
158
  return if pending_evaluations.empty?
147
- @capybara_page.evaluate_ruby(pending_evaluations.collect(&:call).join("\n"))
159
+ @capybara_page.internal_evaluate_ruby(pending_evaluations.collect(&:call).join("\n"))
148
160
  @pending_evaluations ||= []
149
161
  end
150
162
  end
@@ -0,0 +1,73 @@
1
+ module HyperSpec
2
+ module Internal
3
+ module WindowSizing
4
+ private
5
+
6
+ STD_SIZES = {
7
+ small: [480, 320],
8
+ mobile: [640, 480],
9
+ tablet: [960, 640],
10
+ large: [1920, 6000],
11
+ default: [1024, 768]
12
+ }
13
+
14
+ def determine_size(width, height)
15
+ width, height = [height, width] if width == :portrait
16
+ width, height = width if width.is_a? Array
17
+ portrait = true if height == :portrait
18
+ width ||= :default
19
+ width, height = STD_SIZES[width] if STD_SIZES[width]
20
+ width, height = [height, width] if portrait
21
+ [width + debugger_width, height]
22
+ end
23
+
24
+ def debugger_width
25
+ RSpec.configuration.debugger_width ||= begin
26
+ hs_internal_resize_to(1000, 500) do
27
+ sleep RSpec.configuration.wait_for_initialization_time
28
+ end
29
+ inner_width = evaluate_script('window.innerWidth')
30
+ 1000 - inner_width
31
+ end
32
+ RSpec.configuration.debugger_width
33
+ end
34
+
35
+ def hs_internal_resize_to(width, height)
36
+ Capybara.current_session.current_window.resize_to(width, height)
37
+ yield if block_given?
38
+ wait_for_size(width, height)
39
+ end
40
+
41
+ def wait_for_size(width, height)
42
+ @start_time = Capybara::Helpers.monotonic_time
43
+ @stable_count_w = @stable_count_h = 0
44
+ prev_size = [0, 0]
45
+ loop do
46
+ sleep 0.05
47
+ curr_size = evaluate_script('[window.innerWidth, window.innerHeight]')
48
+
49
+ return true if curr_size == [width, height] || stalled?(prev_size, curr_size)
50
+
51
+ prev_size = curr_size
52
+ check_time!
53
+ end
54
+ end
55
+
56
+ def check_time!
57
+ if (Capybara::Helpers.monotonic_time - @start_time) >
58
+ Capybara.current_session.config.default_max_wait_time
59
+ raise Capybara::WindowError,
60
+ 'Window size not stable within '\
61
+ "#{Capybara.current_session.config.default_max_wait_time} seconds."
62
+ end
63
+ end
64
+
65
+ def stalled?(prev_size, curr_size)
66
+ # some maximum or minimum is reached and size doesn't change anymore
67
+ @stable_count_w += 1 if prev_size[0] == curr_size[0]
68
+ @stable_count_h += 1 if prev_size[1] == curr_size[1]
69
+ @stable_count_w > 4 || @stable_count_h > 4
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,67 @@
1
+ require 'hyper-spec'
2
+
3
+ class HyperSpecTestController < SimpleDelegator
4
+ include HyperSpec::ControllerHelpers
5
+
6
+ class << self
7
+ attr_reader :sprocket_server
8
+ attr_reader :asset_path
9
+
10
+ def wrap(app:, append_path: 'app', asset_path: '/assets')
11
+ @sprocket_server = Opal::Sprockets::Server.new do |s|
12
+ s.append_path append_path
13
+ end
14
+
15
+ @asset_path = asset_path
16
+
17
+ ::Rack::Builder.app(app) do
18
+ map "/#{HyperSpecTestController.route_root}" do
19
+ use HyperSpecTestController
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def sprocket_server
26
+ self.class.sprocket_server
27
+ end
28
+
29
+ def asset_path
30
+ self.class.asset_path
31
+ end
32
+
33
+ def ping!
34
+ [204, {}, []]
35
+ end
36
+
37
+ def application!(file)
38
+ @page << Opal::Sprockets.javascript_include_tag(
39
+ file,
40
+ debug: true,
41
+ sprockets: sprocket_server.sprockets,
42
+ prefix: asset_path
43
+ )
44
+ end
45
+
46
+ def json!
47
+ @page << Opal::Sprockets.javascript_include_tag(
48
+ 'json',
49
+ debug: true,
50
+ sprockets: sprocket_server.sprockets,
51
+ prefix: asset_path
52
+ )
53
+ end
54
+
55
+
56
+ def style_sheet!(_file_); end
57
+
58
+ def deliver!
59
+ [200, { 'Content-Type' => 'text/html' }, [@page]]
60
+ end
61
+
62
+ def call(env)
63
+ __setobj__(Rack::Request.new(env))
64
+ params[:id] = path.split('/').last
65
+ test
66
+ end
67
+ end