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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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