hyper-spec 1.0.alpha1.8 → 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.
@@ -1,164 +0,0 @@
1
- module HyperSpec
2
- # Defines a series of methods that will build a test page
3
- # This module is typically included into the HyperSpecTestController class.
4
- module ControllerHelpers
5
- # These methods are dependent on the stack being used. See the
6
- # RailsControllerHelpers module and rack.rb for two implementations.
7
- # Each method should append the appropriate code to the @page array
8
-
9
- # return an empty 204 status either by setting headers or
10
- # returning and appropriate response for rack.
11
- def ping!
12
- raise 'must implement'
13
- end
14
-
15
- def json!
16
- # this can be a no-op but if json is not included by the application,
17
- # hyper-spec will fail, with an error complaining about to_json
18
- end
19
-
20
- # return a script or style_sheet tag pointing to some asset.
21
- # typically you be pointing to a path on the server or using
22
- # sprockets to deliver the file.
23
-
24
- def require!(_file_)
25
- raise 'must implement'
26
- end
27
-
28
- def style_sheet!(_file_)
29
- raise 'must implement'
30
- end
31
-
32
- # deliver the page. The @page variable will contain the html ready to go,
33
- # any additional options that are passed through from the spec will be
34
- # in the @render_params variable. For example layout: 'my_special_layout'
35
-
36
- def deliver!
37
- raise 'must implement'
38
- end
39
-
40
- # generate a react_render top level block. This will only be called if
41
- # you use the mount directive in your specs, so it is optional.
42
-
43
- def mount_component!
44
- raise 'mount_component not implemented in HyperSpecTestController'
45
- end
46
-
47
- # by default the route back to the controller will be the controller name, less the
48
- # word Controller, and underscored. If you want some other name redefine this
49
- # method in the HyperSpecController class.
50
-
51
- def self.included(base)
52
- def base.route_root
53
- # Implement underscore without using rails underscore, so we don't have a
54
- # dependency on ActiveSupport
55
- name.gsub(/Controller$/, '')
56
- .gsub(/::/, '/')
57
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
58
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
59
- .tr('-', '_')
60
- .downcase
61
- end
62
- end
63
-
64
- # The remainder of the methods should work for most implementations.
65
-
66
- # helper method checking the render_on parameter
67
-
68
- def on_client?
69
- @render_on != :server_only
70
- end
71
-
72
- # The controllers behavior is kept as an array of values in the Controller cache
73
- # under a unique id for each test run. Grab the parameters and move them to instance vars
74
-
75
- # If this is just a ping! Then we can just exit with nil.
76
-
77
- def initialize!
78
- return if params[:id] == 'ping'
79
-
80
- key = "/#{self.class.route_root}/#{params[:id]}"
81
- test_params = Internal::Controller.cache_read(key)
82
-
83
- @component_name = test_params[0]
84
- @component_params = test_params[1]
85
- @html_block = test_params[2]
86
- @render_params = test_params[3]
87
- @render_on = @render_params.delete(:render_on) || :client_only
88
- @_mock_time = @render_params.delete(:mock_time)
89
- @style_sheet = @render_params.delete(:style_sheet)
90
- @javascript = @render_params.delete(:javascript)
91
- @code = @render_params.delete(:code)
92
-
93
- @page = ['<body>']
94
- end
95
-
96
- # add any html code generated by the insert_html directive
97
-
98
- def html_block!
99
- @page << @html_block
100
- end
101
-
102
- # patch behavior of the HyperComponent TopLevelRailsComponent class
103
- # so that things like events are passed back to the test harness
104
- TOP_LEVEL_COMPONENT_PATCH =
105
- Opal.compile(File.read(File.expand_path('../sources/top_level_rails_component.rb', __dir__)))
106
-
107
- # patch time cop and lolex so they stay in sync across the client and server
108
- TIME_COP_CLIENT_PATCH =
109
- Opal.compile(File.read(File.expand_path('../hyper-spec/internal/time_cop.rb', __dir__))) +
110
- "\n#{File.read(File.expand_path('../sources/lolex.js', __dir__))}"
111
-
112
- def client_code!
113
- if @component_name
114
- @page << "<script type='text/javascript'>\n#{TOP_LEVEL_COMPONENT_PATCH}\n</script>"
115
- end
116
- @page << "<script type='text/javascript'>\n#{@code}\n</script>" if @code
117
- end
118
-
119
- def time_cop_patch!
120
- @page << "<script type='text/javascript'>\n#{TIME_COP_CLIENT_PATCH}\n</script>"
121
- end
122
-
123
- # Add the go_function to the client console. This is used to stop a hyper-spec pause directive.
124
-
125
- def go_function!
126
- @page << "<script type='text/javascript'>go = function() "\
127
- '{window.hyper_spec_waiting_for_go = false}</script>'
128
- end
129
-
130
- # First lines displayed on the console will be the name of the spec
131
-
132
- def example_title!
133
- title = Internal::Controller.current_example_description!
134
- @page << "<script type='text/javascript'>console.log('%c#{title}',"\
135
- "'color:green; font-weight:bold; font-size: 200%')</script>"
136
- end
137
-
138
- # generate each piece of the page, and then deliver it
139
-
140
- def style_sheet_file
141
- @style_sheet || (!@render_params[:layout] && 'application')
142
- end
143
-
144
- def application_file
145
- @javascript || (on_client? && !@render_params[:layout] && 'application')
146
- end
147
-
148
- def test
149
- return ping! unless initialize!
150
-
151
- html_block!
152
- example_title! if Internal::Controller.current_example
153
- go_function! if on_client?
154
- style_sheet!(style_sheet_file) if style_sheet_file
155
- application!(application_file) if application_file
156
- json!
157
- time_cop_patch! if on_client? || Lolex.initialized?
158
- client_code! if on_client?
159
- mount_component! if @component_name
160
- @page = @page.join("\n") + "\n</body>\n"
161
- deliver!
162
- end
163
- end
164
- end
@@ -1,64 +0,0 @@
1
- # don't put this in directory lib/rspec/ as that will cause stack overflow with rails/rspec loads
2
- module RSpec
3
- module Expectations
4
- class ExpectationTarget; end
5
- module HyperSpecInstanceMethods
6
- def self.included(base)
7
- base.include HyperSpec::Helpers
8
- end
9
-
10
- def to_on_client(matcher, message = nil, &block)
11
- evaluate_client.to(matcher, message, &block)
12
- end
13
-
14
- alias on_client_to to_on_client
15
- alias to_then to_on_client
16
- alias then_to to_on_client
17
-
18
- def to_on_client_not(matcher, message = nil, &block)
19
- evaluate_client.not_to(matcher, message, &block)
20
- end
21
-
22
- alias on_client_to_not to_on_client_not
23
- alias on_client_not_to to_on_client_not
24
- alias to_not_on_client to_on_client_not
25
- alias not_to_on_client to_on_client_not
26
- alias then_to_not to_on_client_not
27
- alias then_not_to to_on_client_not
28
- alias to_not_then to_on_client_not
29
- alias not_to_then to_on_client_not
30
-
31
- private
32
-
33
- def evaluate_client
34
- source = add_opal_block(@args_str, @target)
35
- value = @target.binding.eval("evaluate_ruby(#{source.inspect}, {}, {})")
36
- ExpectationTarget.for(value, nil)
37
- end
38
- end
39
-
40
- class OnClientWithArgsTarget
41
- include HyperSpecInstanceMethods
42
-
43
- def initialize(target, args)
44
- unless args.is_a? Hash
45
- raise ExpectationNotMetError,
46
- "You must pass a hash of local var, value pairs to the 'with' modifier"
47
- end
48
-
49
- @target = target
50
- @args_str = args.collect do |name, value|
51
- set_local_var(name, value)
52
- end.join("\n")
53
- end
54
- end
55
-
56
- class BlockExpectationTarget < ExpectationTarget
57
- include HyperSpecInstanceMethods
58
-
59
- def with(args)
60
- OnClientWithArgsTarget.new(@target, args)
61
- end
62
- end
63
- end
64
- end
@@ -1,225 +0,0 @@
1
- module HyperSpec
2
- module Helpers
3
- include Internal::ClientExecution
4
- include Internal::Controller
5
- include Internal::ComponentMount
6
- include Internal::CopyLocals
7
- include Internal::WindowSizing
8
-
9
- ##
10
- # Mount a component on a page, with a full execution environment
11
- # i.e. `mount('MyComponent')` will mount MyComponent on the page.
12
-
13
- # The params argument is a hash of parameters to be passed to the
14
- # component.
15
- # i.e. `mount('MyComponent', title: 'hello')`
16
-
17
- # The options parameters can set things like:
18
- # + controller: the controller class, defaults to HyperSpecTestController
19
- # + no_wait: do not wait for any JS to finish executing before proceeding with the spec
20
- # + render_on: :client_only (default), :client_and_server, or :server_only
21
- # + style_sheet: style sheet file defaults to 'application'
22
- # + javascript: javascript file defaults to 'application'
23
- # + layout: if provided will use the specified layout otherwise no layout is used
24
- # Note that if specifying options the params will have to inclosed in their
25
- # own hash.
26
- # i.e. `mount('MyComponent', { title: 'hello' }, render_on: :server_only)`
27
- # The options can be specified globally using the client_options method (see below.)
28
-
29
- # You may provide a block to mount. This block will be executed on the client
30
- # before mounting the component. This is useful for setting up test
31
- # components or modifying a components behavior.
32
- # i.e.
33
- # ```ruby
34
- # mount('MyComponent', title: 'hello') do
35
- # # this line will be printed on the client console
36
- # puts "I'm about to mount my component!"
37
- # end
38
- # ```
39
- def mount(component_name = nil, params = nil, opts = {}, &block)
40
- unless params
41
- params = opts
42
- opts = {}
43
- end
44
- internal_mount(component_name, params, client_options(opts), &block)
45
- end
46
-
47
- ##
48
- # The following methods retrieve callback and event responses from
49
- # the mounted components. The history methods contain the array of all
50
- # responses, while last_... returns the last response.
51
- # i.e. event_history_for(:save) would return any save events
52
- # that the component has raised.
53
-
54
- %i[
55
- callback_history_for last_callback_for clear_callback_history_for
56
- event_history_for last_event_for clear_event_history_for
57
- ].each do |method|
58
- define_method(method) do |event_name|
59
- evaluate_ruby(
60
- "Hyperstack::Internal::Component::TopLevelRailsComponent.#{method}('#{event_name}')"
61
- )
62
- end
63
- end
64
-
65
- ##
66
- # Define a code block to be prefixed to the mount code.
67
- # Useful in before(:each) blocks.
68
-
69
- # In legacy code this was called `on_client`. To get the legacy
70
- # behavior alias on_client before_mount
71
- # but be aware that on_client is now by default the method
72
- # for executing a block of code on the client which was called
73
- # evaluate_ruby
74
-
75
- def before_mount(&block)
76
- @_hyperspec_private_client_code =
77
- "#{@_hyperspec_private_client_code}#{add_opal_block('', block)}"
78
- end
79
-
80
- # Execute the block both on the client and on the server. Useful
81
- # for mocking isomorphic classes such as ActiveRecord models.
82
-
83
- def isomorphic(&block)
84
- yield
85
- if page.instance_variable_get('@hyper_spec_mounted')
86
- internal_evaluate_ruby(&block)
87
- else
88
- before_mount(&block)
89
- end
90
- end
91
-
92
- # Allows options to the mount method to be specified globally
93
-
94
- def client_option(opts = {})
95
- @_hyperspec_private_client_options ||= { arity_check: default_arity_check }
96
- @_hyperspec_private_client_options.merge! opts
97
- build_var_inclusion_lists
98
- @_hyperspec_private_client_options
99
- end
100
-
101
- def default_arity_check
102
- Rails.application.config.opal.arity_check_enabled if defined? Rails
103
- rescue StandardError
104
- false
105
- end
106
-
107
- alias client_options client_option
108
-
109
- ##
110
- # shorthand for mount with no params (which will effectively reload the page.)
111
- # also aliased as reload_page
112
- def load_page
113
- mount
114
- end
115
-
116
- alias reload_page load_page
117
-
118
- ##
119
- # evaluate a block (or string) on the client
120
- # on_client(<optional str>, <opts and/or vars>, &block)
121
- #
122
- # normal use is to pass a block that will be compiled to the client
123
- # but if the ruby code can be supplied as a string in the first arg.
124
-
125
- # opts are passed on to JSON.parse when retrieving the result
126
- # from the client.
127
-
128
- # vars is a hash of name: value pairs. Each name will be initialized
129
- # as a local variable on the client.
130
-
131
- # example: on_client(x: 12) { x * x } => 144
132
-
133
- # in legacy code on_client was called before_mount
134
- # to get legacy on_client behavior you can alias
135
- # on_client before_mount
136
-
137
- alias on_client internal_evaluate_ruby
138
-
139
- # attempt to set the window to a particular size
140
-
141
- def size_window(width = nil, height = nil)
142
- hs_internal_resize_to(*determine_size(width, height))
143
- rescue StandardError
144
- true
145
- end
146
-
147
- # same signature as on_client, but just returns the compiled
148
- # js code. Useful for debugging suspected issues with the
149
- # Opal compiler, etc.
150
-
151
- def to_js(*args, &block)
152
- opal_compile(*process_params(*args, &block))
153
- end
154
-
155
- # legacy methods for backwards compatibility
156
- # these may be removed in a future version
157
-
158
- def expect_evaluate_ruby(*args, &block)
159
- expect(evaluate_ruby(*args, &block))
160
- end
161
-
162
- alias evaluate_ruby internal_evaluate_ruby
163
- alias evaluate_promise evaluate_ruby
164
- alias expect_promise expect_evaluate_ruby
165
-
166
- def run_on_client(&block)
167
- script = opal_compile(Unparser.unparse(Parser::CurrentRuby.parse(block.source).children.last))
168
- page.execute_script(script)
169
- end
170
-
171
- def insert_html(str)
172
- @_hyperspec_private_html_block = "#{@_hyperspec_private_html_block}\n#{str}"
173
- end
174
-
175
- def ppr(str)
176
- js = opal_compile(str)
177
- execute_script("console.log(#{js})")
178
- end
179
-
180
- def debugger
181
- `debugger`
182
- nil
183
- end
184
-
185
- def add_class(class_name, style)
186
- @_hyperspec_private_client_code =
187
- "#{@_hyperspec_private_client_code}ComponentHelpers.add_class('#{class_name}', #{style})\n"
188
- end
189
-
190
- def attributes_on_client(model)
191
- evaluate_ruby("#{model.class.name}.find(#{model.id}).attributes").symbolize_keys
192
- end
193
-
194
- ### --- Debugging Helpers ----
195
-
196
- def pause(message = nil)
197
- if message
198
- puts message
199
- internal_evaluate_ruby "puts #{message.inspect}.to_s + ' (type go() to continue)'"
200
- end
201
-
202
- page.evaluate_script('window.hyper_spec_waiting_for_go = true')
203
-
204
- loop do
205
- sleep 0.25
206
- break unless page.evaluate_script('window.hyper_spec_waiting_for_go')
207
- end
208
- end
209
-
210
- def open_in_chrome
211
- # if ['linux', 'freebsd'].include?(`uname`.downcase)
212
- # `google-chrome http://#{page.server.host}:#{page.server.port}#{page.current_path}`
213
- # else
214
- `open http://#{page.server.host}:#{page.server.port}#{page.current_path}`
215
- # end
216
-
217
- loop do
218
- sleep 1.hour
219
- end
220
- end
221
-
222
- # short hand for use in pry sessions
223
- alias c? internal_evaluate_ruby
224
- end
225
- end