marty 2.4.0 → 2.4.1

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.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile.lock +1 -1
  3. data/app/components/marty/api_auth_view.rb +4 -27
  4. data/app/components/marty/extras/layout.rb +7 -1
  5. data/app/components/marty/grid.rb +10 -9
  6. data/app/controllers/marty/rpc_controller.rb +4 -2
  7. data/app/models/marty/api_auth.rb +1 -80
  8. data/app/models/marty/api_config.rb +4 -4
  9. data/app/models/marty/delorean_rule.rb +2 -3
  10. data/config/routes.rb +1 -1
  11. data/db/migrate/{501_add_api_class_to_marty_api_config.rb → 500_add_api_class_to_marty_api_config.rb} +0 -0
  12. data/lib/marty/aws/base.rb +98 -0
  13. data/lib/marty/util.rb +15 -0
  14. data/lib/marty/version.rb +1 -1
  15. data/other/marty/api/base.rb +3 -0
  16. data/spec/controllers/job_controller_spec.rb +1 -1
  17. data/spec/dummy/app/components/gemini/xyz_rule_view.rb +0 -1
  18. data/spec/dummy/config/application.rb +0 -1
  19. data/spec/features/enum_spec.rb +100 -35
  20. data/spec/features/log_view_spec.rb +5 -5
  21. data/spec/features/rule_spec.rb +30 -30
  22. data/spec/features/user_view_spec.rb +2 -0
  23. data/spec/lib/logger_spec.rb +1 -1
  24. data/spec/models/event_spec.rb +1 -1
  25. data/spec/models/promise_spec.rb +1 -1
  26. data/spec/models/user_spec.rb +6 -6
  27. data/spec/spec_helper.rb +9 -69
  28. data/spec/support/chromedriver.rb +41 -0
  29. data/spec/support/components/netzke_combobox.rb +57 -0
  30. data/spec/support/components/netzke_grid.rb +356 -0
  31. data/spec/support/custom_matchers.rb +18 -0
  32. data/spec/support/custom_selectors.rb +49 -0
  33. data/spec/support/delayed_job_helpers.rb +4 -5
  34. data/spec/support/download_helper.rb +52 -0
  35. data/spec/support/helper.rb +20 -0
  36. data/spec/support/netzke.rb +306 -0
  37. data/spec/support/performance_helper.rb +26 -0
  38. data/spec/support/post_run_logger.rb +32 -0
  39. data/spec/support/{spec_setup.rb → setup.rb} +19 -6
  40. data/spec/support/shared_connection.rb +31 -0
  41. data/spec/support/{clean_db_helpers.rb → shared_connection_db_helpers.rb} +2 -2
  42. data/spec/support/structure_compare.rb +62 -0
  43. data/spec/support/suite.rb +27 -0
  44. data/spec/support/{integration_helpers.rb → users.rb} +11 -9
  45. metadata +20 -8
  46. data/db/migrate/502_add_parameters_to_marty_api_auth.rb +0 -5
  47. data/spec/support/user_helpers.rb +0 -12
@@ -0,0 +1,18 @@
1
+ require 'rspec'
2
+
3
+ RSpec::Matchers.define :netzke_include do |expected|
4
+ match do |actual|
5
+ parsed_values = actual.each_with_object({}) do | (k, v), h |
6
+ h[k] = v == "False" ? false : v
7
+ end
8
+ expect(parsed_values).to include(expected.stringify_keys)
9
+ end
10
+
11
+ diffable
12
+ end
13
+
14
+ RSpec::Matchers.define :match_fuzzily do |expected|
15
+ msg = nil
16
+ match { |actual| !(msg = struct_compare(actual, expected)) }
17
+ failure_message { |_| msg }
18
+ end
@@ -0,0 +1,49 @@
1
+ Capybara.add_selector(:gridpanel) do
2
+ xpath do |name|
3
+ ".//div[contains(@id, '#{name}')][not(contains(@id, 'splitter'))] | "\
4
+ ".//div[contains(@id, '#{name.camelize(:lower)}')]"\
5
+ "[not(contains(@id, 'splitter'))]"
6
+ end
7
+ end
8
+
9
+ Capybara.add_selector(:msg) do
10
+ xpath do
11
+ "//div[@id='msg-div']"
12
+ end
13
+ end
14
+
15
+ Capybara.add_selector(:body) do
16
+ xpath do
17
+ ".//div[@data-ref='body']"
18
+ end
19
+ end
20
+
21
+ Capybara.add_selector(:input) do
22
+ xpath do |name|
23
+ "//input[@name='#{name}']"
24
+ end
25
+ end
26
+
27
+ Capybara.add_selector(:status) do
28
+ xpath do |name|
29
+ "//div[contains(@id, 'statusbar')]//div[text()='#{name}']"
30
+ end
31
+ end
32
+
33
+ Capybara.add_selector(:btn) do
34
+ xpath do |name|
35
+ ".//span[text()='#{name}']"
36
+ end
37
+ end
38
+
39
+ Capybara.add_selector(:refresh) do
40
+ xpath do
41
+ ".//div[contains(@class, 'x-tool-refresh')]"
42
+ end
43
+ end
44
+
45
+ Capybara.add_selector(:gridcolumn) do
46
+ xpath do |name|
47
+ ".//span[contains(@class, 'x-column-header')][text()='#{name}']/.."
48
+ end
49
+ end
@@ -1,12 +1,11 @@
1
- module DelayedJobHelpers
1
+ module Marty::RSpec::DelayedJobHelpers
2
2
  def start_delayed_job
3
- # start delayed job workers and wait a few seconds
4
- `RAILS_ENV=test spec/dummy/script/delayed_job -n 4 stop | cat`
5
- `RAILS_ENV=test spec/dummy/script/delayed_job -n 4 start | cat`
3
+ `RAILS_ENV=test #{Rails.root}/script/delayed_job -n 4 stop | cat`
4
+ `RAILS_ENV=test #{Rails.root}/script/delayed_job -n 4 start | cat`
6
5
  sleep 5
7
6
  end
8
7
 
9
8
  def stop_delayed_job
10
- `RAILS_ENV=test spec/dummy/script/delayed_job -n 4 stop | cat`
9
+ `RAILS_ENV=test #{Rails.root}/script/delayed_job -n 4 stop | cat`
11
10
  end
12
11
  end
@@ -0,0 +1,52 @@
1
+ module Marty::RSpec::DownloadHelper
2
+ TIMEOUT = 10
3
+ PATH = Rails.root.join('spec/tmp/downloads')
4
+
5
+ ACCEPTED_EXTS = ['.xlsx', '.csv']
6
+
7
+ extend self
8
+
9
+ def downloads
10
+ Dir[PATH.join("*")]
11
+ end
12
+
13
+ def download
14
+ downloads.first
15
+ end
16
+
17
+ def download_content
18
+ wait_for_download
19
+ # doesn't work for excel files...
20
+ File.read(download)
21
+ end
22
+
23
+ def download_content_acceptable?
24
+ wait_for_download
25
+ downloads.each do |f|
26
+ return false unless ACCEPTED_EXTS.include? File.extname(f)
27
+ end
28
+ true
29
+ end
30
+
31
+ def wait_for_download
32
+ Timeout.timeout(TIMEOUT) do
33
+ sleep 0.1 until downloaded?
34
+ end
35
+ end
36
+
37
+ def downloaded?
38
+ downloads.any? && !downloading?
39
+ end
40
+
41
+ def downloading?
42
+ downloads.grep(/\.part$/).any? ||
43
+ downloads.select { |f| File.size(f).zero? }.any?
44
+ end
45
+
46
+ def clear_downloads
47
+ FileUtils.rm_f(downloads)
48
+ Timeout.timeout(TIMEOUT) do
49
+ sleep 0.1 until !downloads.any?
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,20 @@
1
+ require 'delorean_lang'
2
+
3
+ module Marty; module RSpec;
4
+ class Helper
5
+ include Delorean::Model
6
+ # Helper function which increments a global counter. Can be used by
7
+ # tests which run Delorean code to see how many times some code is
8
+ # being called. Works for rule scripts as well.
9
+ delorean_fn :global_inc, sig: 1 do
10
+ |inc|
11
+ @@global_inc ||= 0
12
+
13
+ if inc
14
+ @@global_inc += inc
15
+ else
16
+ @@global_inc = 0
17
+ end
18
+ end
19
+ end
20
+ end end
@@ -0,0 +1,306 @@
1
+ module Marty; module RSpec; module Netzke
2
+ MAX_WAIT_TIME = 5.0
3
+
4
+ def by message, level=0
5
+ wait_for_ready(10)
6
+ pending(message) unless block_given?
7
+ yield
8
+ end
9
+
10
+ alias and_by by
11
+
12
+ ############################################################################
13
+ # navigation helpers
14
+ ############################################################################
15
+
16
+ def ensure_on(path)
17
+ visit(path) unless current_path == path
18
+ end
19
+
20
+ def log_in(username, password)
21
+ wait_for_ready(10)
22
+
23
+ begin
24
+ if first("a[data-qtip='Current user']")
25
+ log_out
26
+ wait_for_ajax
27
+ end
28
+ rescue
29
+ # ignore error
30
+ end
31
+
32
+ find(:xpath, "//span", text: 'Sign in', match: :first, wait: 5).click
33
+ fill_in("login", :with => username)
34
+ fill_in("password", :with => password)
35
+ press("OK")
36
+ wait_for_ajax
37
+ end
38
+
39
+ def log_in_as(username)
40
+ Rails.configuration.marty.auth_source = 'local'
41
+
42
+ ensure_on("/")
43
+ log_in(username, Rails.configuration.marty.local_password)
44
+ ensure_on("/")
45
+ end
46
+
47
+ def log_out
48
+ press("Current user")
49
+ press("Sign out")
50
+ end
51
+
52
+ def press button_name, index_of = 0
53
+ wait_for_element do
54
+ begin
55
+ cmp = first("a[data-qtip='#{button_name}']")
56
+ cmp ||= first(:xpath, ".//a", text: "#{button_name}")
57
+ cmp ||= find(:btn, button_name, match: :first)
58
+ cmp.click
59
+ true
60
+ rescue
61
+ find_by_id(ext_button_id(button_name, index_of), visible: :all).click
62
+ true
63
+ end
64
+ end
65
+ end
66
+
67
+ def popup message = ''
68
+ wait_for_ready
69
+ yield if block_given?
70
+ close_window
71
+ end
72
+
73
+ def close_window
74
+ find(:xpath, '//div[contains(@class, "x-tool-close")]', wait: 5).click
75
+ end
76
+
77
+ ############################################################################
78
+ # stability functions
79
+ ############################################################################
80
+
81
+ def wait_for_ready wait_time = nil
82
+ if wait_time
83
+ find(:status, 'Ready', wait: wait_time)
84
+ else
85
+ find(:status, 'Ready')
86
+ end
87
+ end
88
+
89
+ def wait_for_ajax wait_time = 10
90
+ wait_for_ready(wait_time)
91
+ wait_for_element { !ajax_loading? }
92
+ wait_for_ready
93
+ end
94
+
95
+ def ajax_loading?
96
+ page.execute_script <<-JS
97
+ return Netzke.ajaxIsLoading() || Ext.Ajax.isLoading();
98
+ JS
99
+ end
100
+
101
+ def wait_for_element(seconds_to_wait = 2.0, sleeptime = 0.1)
102
+ res = nil
103
+ start_time = current_time = Time.now
104
+ while !res && current_time - start_time < seconds_to_wait
105
+ begin
106
+ res = yield
107
+ rescue
108
+ ensure
109
+ sleep sleeptime
110
+ current_time = Time.now
111
+ end
112
+ end
113
+ res
114
+ end
115
+
116
+ ############################################################################
117
+ # note that netzke_find doesn't actually find the component (as in Capybara)
118
+ # instead, it prepares the javascript to be run on the component object
119
+ ############################################################################
120
+
121
+ def netzke_find(name, c_type = 'gridpanel')
122
+ case c_type
123
+ when 'combobox'
124
+ Marty::RSpec::Components::NetzkeCombobox.new(name)
125
+ else
126
+ Marty::RSpec::Components::NetzkeGrid.new(name, c_type)
127
+ end
128
+ end
129
+
130
+ def run_js js_str, seconds_to_wait = MAX_WAIT_TIME, sleeptime = 0.1
131
+ result = wait_for_element(seconds_to_wait, sleeptime) do
132
+ page.document.synchronize { @res = page.execute_script(js_str) }
133
+ @res
134
+ end
135
+ result
136
+ end
137
+
138
+ ############################################################################
139
+ # component helpers
140
+ ############################################################################
141
+
142
+ def show_submenu text
143
+ run_js <<-JS
144
+ Ext.ComponentQuery.query('menuitem[text="#{text}"] menu')[0].show()
145
+ JS
146
+ end
147
+
148
+ def ext_button_id title, scope = nil, index_of = 0
149
+ c_str = ext_arg('button{isVisible(true)}', text: "\"#{title}\"")
150
+ run_js <<-JS
151
+ return #{ext_find(c_str, scope, index_of)}.id;
152
+ JS
153
+ end
154
+
155
+ def set_field_value value, field_type='textfield', name=''
156
+ args1 = name.empty? ? "" : "[fieldLabel='#{name}']"
157
+ args2 = name.empty? ? "" : "[name='#{name}']"
158
+ run_js <<-JS
159
+ var field = Ext.ComponentQuery.query("#{field_type}#{args1}")[0];
160
+ field = field || Ext.ComponentQuery.query("#{field_type}#{args2}")[0];
161
+ field.setValue("#{value}");
162
+ return true;
163
+ JS
164
+ end
165
+
166
+ def get_total_pages
167
+ # will get deprecated by Netzke 1.0
168
+ result = find(:xpath, ".//div[contains(@id, 'tbtext-')]",
169
+ text: /^of (\d+)$/, match: :first).text
170
+ result.split(' ')[1].to_i
171
+ end
172
+
173
+ ############################################################################
174
+ # helpers
175
+ ############################################################################
176
+
177
+ def id_of component
178
+ res = run_js <<-JS
179
+ var c = #{component};
180
+ return c.view.id;
181
+ JS
182
+ res
183
+ end
184
+
185
+ def btn_disabled? text
186
+ res = wait_for_element do
187
+ find_by_id(ext_button_id(text))
188
+ end
189
+ !res[:class].match(/disabled/).nil?
190
+ end
191
+
192
+ def click_checkbox(name)
193
+ q = %Q(checkbox[fieldLabel="#{name}"])
194
+ item_id = run_js "return Ext.ComponentQuery.query('#{q}')[0].getItemId();"
195
+ find_by_id(item_id).click
196
+ end
197
+
198
+ def paste text, textarea
199
+ # bit hacky: textarea doesn't like receiving tabs and newlines via fill_in
200
+ simple_escape!(text)
201
+
202
+ find(:xpath, ".//textarea[@name='#{textarea}']")
203
+ run_js <<-JS
204
+ #{ext_var(ext_find(ext_arg('textarea', name: textarea)), 'area')}
205
+ area.setValue("#{text}");
206
+ JS
207
+ end
208
+
209
+ def press_key_in(key, el_id)
210
+ kd = key.downcase
211
+ use_key = ['enter', 'return'].include?(kd) ? kd.to_sym : key
212
+ el = find_by_id("#{el_id}")
213
+ el.native.send_keys(use_key)
214
+ end
215
+
216
+ def simple_escape! text
217
+ text.gsub!(/(\r\n|\n)/, "\\n")
218
+ text.gsub!(/\t/, "\\t")
219
+ end
220
+
221
+ def simple_escape text
222
+ text.gsub(/(\r\n|\n)/, "\\n")
223
+ .gsub(/\t/, "\\t")
224
+ .gsub(/"/, '\"')
225
+ end
226
+
227
+ def type_in(type_s, el_id)
228
+ el = find_by_id("#{el_id}")
229
+ el.native.clear()
230
+ type_s.each_char do |key|
231
+ el.native.send_keys(key)
232
+ end
233
+ el.send_keys(:enter)
234
+ end
235
+
236
+ private
237
+ ############################################################################
238
+ # ExtJS/Netzke helper javascripts:
239
+ # Netzke component lookups, arguments for helper methods
240
+ # (i.e. component) require JS scripts instead of objects
241
+ ############################################################################
242
+
243
+ def ext_arg(component, c_args = {})
244
+ res = component
245
+ c_args.each do |k, v|
246
+ res += "[#{k.to_s}=#{v.to_s}]"
247
+ end
248
+ res
249
+ end
250
+
251
+ def ext_find(ext_arg_str, scope = nil, index = 0)
252
+ scope_str = scope.nil? ? '' : ", #{scope}"
253
+ <<-JS
254
+ Ext.ComponentQuery.query('#{ext_arg_str}'#{scope_str})[#{index}]
255
+ JS
256
+ end
257
+
258
+ def ext_var(ext_find_str, var_name='ext_c')
259
+ <<-JS
260
+ var #{var_name} = #{ext_find_str};
261
+ JS
262
+ end
263
+
264
+ def ext_netzkecombo field
265
+ <<-JS
266
+ #{ext_find(ext_arg('netzkeremotecombo', name: field))}
267
+ JS
268
+ end
269
+
270
+ def ext_combo combo_label, c_name='combo'
271
+ <<-JS
272
+ #{ext_var(ext_find(ext_arg('combobox', fieldLabel: combo_label)), c_name)}
273
+ #{c_name} = #{c_name} ||
274
+ #{ext_find(ext_arg('combobox', name: combo_label))};
275
+ JS
276
+ end
277
+
278
+ def ext_celleditor(grid_name='grid')
279
+ <<-JS
280
+ #{grid_name}.getPlugin('celleditor')
281
+ JS
282
+ end
283
+
284
+ def ext_row(row, grid_name='grid')
285
+ <<-JS
286
+ #{grid_name}.getStore().getAt(#{row})
287
+ JS
288
+ end
289
+
290
+ def ext_col(col, grid_name='grid')
291
+ <<-JS
292
+ #{ext_find(ext_arg('gridcolumn', name: "\"#{col}\""), grid_name)}
293
+ JS
294
+ end
295
+
296
+ def ext_cell_val(row, col, grid, var_str = 'value')
297
+ <<-JS
298
+ #{ext_var(grid, 'grid')}
299
+ #{ext_var(ext_col(col, 'grid'), 'col')}
300
+ #{ext_var(ext_row(row, 'grid'), 'row')}
301
+ var #{var_str} = col.assoc ?
302
+ row.get('association_values')['#{col}'] :
303
+ row.get('#{col}');
304
+ JS
305
+ end
306
+ end end end