api_maker 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/app/api_maker/api_helpers/api_maker_helpers.rb +5 -0
  3. data/app/api_maker/services/can_can/load_abilities.rb +30 -0
  4. data/app/api_maker/services/devise/sign_in.rb +64 -0
  5. data/app/api_maker/services/devise/sign_out.rb +9 -0
  6. data/app/api_maker/services/models/find_or_create_by.rb +18 -0
  7. data/app/channels/api_maker/subscriptions_channel.rb +33 -2
  8. data/app/controllers/api_maker/base_controller.rb +7 -3
  9. data/app/controllers/api_maker/commands_controller.rb +26 -4
  10. data/app/controllers/api_maker/session_statuses_controller.rb +1 -1
  11. data/app/services/api_maker/abilities_loader.rb +104 -0
  12. data/app/services/api_maker/application_service.rb +2 -1
  13. data/app/services/api_maker/base_command.rb +248 -0
  14. data/app/services/api_maker/collection_command_service.rb +29 -15
  15. data/app/services/api_maker/collection_loader.rb +124 -0
  16. data/app/services/api_maker/command_failed_error.rb +3 -0
  17. data/app/services/api_maker/command_response.rb +17 -6
  18. data/app/services/api_maker/command_service.rb +3 -3
  19. data/app/services/api_maker/create_command.rb +11 -26
  20. data/app/services/api_maker/create_command_service.rb +3 -3
  21. data/app/services/api_maker/database_type.rb +9 -0
  22. data/app/services/api_maker/deep_merge_params.rb +26 -0
  23. data/app/services/api_maker/deserializer.rb +35 -0
  24. data/app/services/api_maker/destroy_command.rb +15 -21
  25. data/app/services/api_maker/destroy_command_service.rb +3 -3
  26. data/app/services/api_maker/generate_react_native_api_service.rb +3 -19
  27. data/app/services/api_maker/include_helpers.rb +17 -0
  28. data/app/services/api_maker/index_command.rb +8 -88
  29. data/app/services/api_maker/index_command_service.rb +5 -5
  30. data/app/services/api_maker/js_method_namer_service.rb +1 -1
  31. data/app/services/api_maker/locals_from_controller.rb +14 -0
  32. data/app/services/api_maker/member_command_service.rb +15 -13
  33. data/app/services/api_maker/model_classes_java_script_generator_service.rb +37 -0
  34. data/app/services/api_maker/model_content_generator_service.rb +17 -21
  35. data/app/services/api_maker/models/save.rb +29 -0
  36. data/app/services/api_maker/models_finder_service.rb +6 -2
  37. data/app/services/api_maker/models_generator_service.rb +6 -43
  38. data/app/services/api_maker/move_components_to_routes.rb +50 -0
  39. data/app/services/api_maker/primary_id_for_model.rb +6 -0
  40. data/app/services/api_maker/reset_indexed_db_service.rb +36 -0
  41. data/app/services/api_maker/routes_file_reloader.rb +20 -0
  42. data/app/services/api_maker/select_columns_on_collection.rb +78 -0
  43. data/app/services/api_maker/select_parser.rb +32 -0
  44. data/app/services/api_maker/service_command.rb +27 -0
  45. data/app/services/api_maker/service_command_service.rb +14 -0
  46. data/app/services/api_maker/simple_model_errors.rb +52 -0
  47. data/app/services/api_maker/update_command.rb +8 -24
  48. data/app/services/api_maker/update_command_service.rb +3 -3
  49. data/app/services/api_maker/valid_command.rb +4 -13
  50. data/app/services/api_maker/valid_command_service.rb +3 -3
  51. data/app/services/api_maker/validation_errors_generator_service.rb +146 -0
  52. data/app/views/api_maker/_data.html.erb +17 -11
  53. data/config/routes.rb +0 -2
  54. data/lib/api_maker/ability.rb +22 -7
  55. data/lib/api_maker/ability_loader.rb +9 -6
  56. data/lib/api_maker/base_collection_instance.rb +15 -0
  57. data/lib/api_maker/base_resource.rb +135 -9
  58. data/lib/api_maker/base_service.rb +14 -0
  59. data/lib/api_maker/collection_serializer.rb +95 -34
  60. data/lib/api_maker/command_spec_helper.rb +41 -11
  61. data/lib/api_maker/configuration.rb +31 -4
  62. data/lib/api_maker/expect_to_able_to_helper.rb +31 -0
  63. data/lib/api_maker/individual_command.rb +24 -9
  64. data/lib/api_maker/javascript/model-template.js.erb +39 -25
  65. data/lib/api_maker/javascript/models.js.erb +6 -0
  66. data/lib/api_maker/loader.rb +1 -1
  67. data/lib/api_maker/memory_storage.rb +1 -1
  68. data/lib/api_maker/model_extensions.rb +34 -18
  69. data/lib/api_maker/permitted_params_argument.rb +5 -1
  70. data/lib/api_maker/preloader.rb +71 -32
  71. data/lib/api_maker/preloader_base.rb +108 -0
  72. data/lib/api_maker/preloader_belongs_to.rb +34 -33
  73. data/lib/api_maker/preloader_has_many.rb +45 -39
  74. data/lib/api_maker/preloader_has_one.rb +30 -47
  75. data/lib/api_maker/railtie.rb +3 -11
  76. data/lib/api_maker/relationship_preloader.rb +42 -0
  77. data/lib/api_maker/resource_routing.rb +18 -4
  78. data/lib/api_maker/result_parser.rb +34 -20
  79. data/lib/api_maker/serializer.rb +53 -22
  80. data/lib/api_maker/spec_helper/browser_logs.rb +14 -0
  81. data/lib/api_maker/spec_helper/execute_collection_command.rb +46 -0
  82. data/lib/api_maker/spec_helper/execute_member_command.rb +52 -0
  83. data/lib/api_maker/spec_helper/expect_no_browser_errors.rb +18 -0
  84. data/lib/api_maker/spec_helper/wait_for_expect.rb +20 -0
  85. data/lib/api_maker/spec_helper/wait_for_flash_message.rb +21 -0
  86. data/lib/api_maker/spec_helper.rb +112 -48
  87. data/lib/api_maker/version.rb +1 -1
  88. data/lib/api_maker.rb +7 -3
  89. metadata +108 -89
  90. data/README.md +0 -476
  91. data/app/controllers/api_maker/devise_controller.rb +0 -60
  92. data/lib/api_maker/base_command.rb +0 -81
  93. data/lib/api_maker/javascript/api.js +0 -92
  94. data/lib/api_maker/javascript/base-model.js +0 -543
  95. data/lib/api_maker/javascript/bootstrap/attribute-row.jsx +0 -16
  96. data/lib/api_maker/javascript/bootstrap/attribute-rows.jsx +0 -47
  97. data/lib/api_maker/javascript/bootstrap/card.jsx +0 -79
  98. data/lib/api_maker/javascript/bootstrap/checkbox.jsx +0 -127
  99. data/lib/api_maker/javascript/bootstrap/checkboxes.jsx +0 -105
  100. data/lib/api_maker/javascript/bootstrap/live-table.jsx +0 -168
  101. data/lib/api_maker/javascript/bootstrap/money-input.jsx +0 -136
  102. data/lib/api_maker/javascript/bootstrap/radio-buttons.jsx +0 -80
  103. data/lib/api_maker/javascript/bootstrap/select.jsx +0 -168
  104. data/lib/api_maker/javascript/bootstrap/string-input.jsx +0 -203
  105. data/lib/api_maker/javascript/cable-connection-pool.js +0 -169
  106. data/lib/api_maker/javascript/cable-subscription-pool.js +0 -111
  107. data/lib/api_maker/javascript/cable-subscription.js +0 -33
  108. data/lib/api_maker/javascript/collection.js +0 -186
  109. data/lib/api_maker/javascript/commands-pool.js +0 -123
  110. data/lib/api_maker/javascript/custom-error.js +0 -14
  111. data/lib/api_maker/javascript/deserializer.js +0 -35
  112. data/lib/api_maker/javascript/devise.js.erb +0 -113
  113. data/lib/api_maker/javascript/error-logger.js +0 -119
  114. data/lib/api_maker/javascript/event-connection.jsx +0 -24
  115. data/lib/api_maker/javascript/event-created.jsx +0 -26
  116. data/lib/api_maker/javascript/event-destroyed.jsx +0 -26
  117. data/lib/api_maker/javascript/event-emitter-listener.jsx +0 -32
  118. data/lib/api_maker/javascript/event-listener.jsx +0 -41
  119. data/lib/api_maker/javascript/event-updated.jsx +0 -26
  120. data/lib/api_maker/javascript/form-data-to-object.js +0 -70
  121. data/lib/api_maker/javascript/included.js +0 -39
  122. data/lib/api_maker/javascript/key-value-store.js +0 -47
  123. data/lib/api_maker/javascript/logger.js +0 -23
  124. data/lib/api_maker/javascript/model-name.js +0 -21
  125. data/lib/api_maker/javascript/models-response-reader.js +0 -43
  126. data/lib/api_maker/javascript/paginate.jsx +0 -128
  127. data/lib/api_maker/javascript/params.js +0 -68
  128. data/lib/api_maker/javascript/resource-route.jsx +0 -75
  129. data/lib/api_maker/javascript/resource-routes.jsx +0 -36
  130. data/lib/api_maker/javascript/result.js +0 -25
  131. data/lib/api_maker/javascript/session-status-updater.js +0 -113
  132. data/lib/api_maker/javascript/sort-link.jsx +0 -88
  133. data/lib/api_maker/javascript/updated-attribute.jsx +0 -60
  134. data/lib/api_maker/preloader_through.rb +0 -101
  135. data/lib/api_maker/relationship_includer.rb +0 -42
@@ -1,14 +1,14 @@
1
1
  class ApiMaker::ResultParser
2
- attr_reader :ability, :args
2
+ attr_reader :ability, :api_maker_args
3
3
 
4
4
  def self.parse(*args)
5
5
  ApiMaker::ResultParser.new(*args).result
6
6
  end
7
7
 
8
- def initialize(object, ability: nil, args: nil, controller: nil)
8
+ def initialize(object, ability: nil, api_maker_args: nil, controller: nil)
9
9
  @object = object
10
10
  @ability = ability || controller&.__send__(:current_ability)
11
- @args = args || controller&.__send__(:api_maker_args) || {}
11
+ @api_maker_args = api_maker_args || controller&.__send__(:api_maker_args) || {}
12
12
  end
13
13
 
14
14
  def result
@@ -17,32 +17,46 @@ class ApiMaker::ResultParser
17
17
 
18
18
  private
19
19
 
20
+ def parse_active_record(object)
21
+ serializer = ApiMaker::Serializer.new(ability: ability, api_maker_args: api_maker_args, model: object)
22
+
23
+ {
24
+ api_maker_type: :model,
25
+ model_name: serializer.resource.collection_name,
26
+ serialized: parse_object(serializer.as_json)
27
+ }
28
+ end
29
+
30
+ def parse_array(object)
31
+ object.map { |value| parse_object(value) }
32
+ end
33
+
34
+ def parse_hash(object)
35
+ result = {}
36
+ object.each do |key, value|
37
+ result[key] = parse_object(value)
38
+ end
39
+
40
+ result
41
+ end
42
+
20
43
  def parse_object(object)
21
44
  if object.is_a?(Hash)
22
- result = {}
23
- object.each do |key, value|
24
- result[key] = parse_object(value)
25
- end
26
-
27
- result
45
+ parse_hash(object)
28
46
  elsif object.is_a?(Array)
29
- object.map { |value| parse_object(value) }
30
- elsif object.class.name == "Money"
47
+ parse_array(object)
48
+ elsif object.class.name == "Money" # rubocop:disable Style/ClassEqualityComparison
31
49
  {api_maker_type: :money, amount: object.cents, currency: object.currency.iso_code}
32
50
  elsif object.is_a?(Date)
33
- object.iso8601
51
+ {api_maker_type: :date, value: object.iso8601}
34
52
  elsif object.is_a?(Time)
35
- object.utc.iso8601
53
+ {api_maker_type: :time, value: object.utc.iso8601}
36
54
  elsif object.is_a?(ApiMaker::CollectionSerializer) || object.is_a?(ApiMaker::Serializer)
37
55
  parse_object(object.as_json)
56
+ elsif object.is_a?(Class) && object < ApiMaker::BaseResource
57
+ {api_maker_type: :resource, name: object.short_name}
38
58
  elsif object.is_a?(ActiveRecord::Base)
39
- serializer = ApiMaker::Serializer.new(ability: ability, args: args, model: object)
40
-
41
- {
42
- api_maker_type: :model,
43
- model_name: serializer.resource.collection_name,
44
- serialized: parse_object(serializer.as_json)
45
- }
59
+ parse_active_record(object)
46
60
  else
47
61
  object
48
62
  end
@@ -1,5 +1,7 @@
1
1
  class ApiMaker::Serializer
2
- attr_reader :ability, :args, :model
2
+ attr_reader :ability, :api_maker_args, :locals, :model
3
+
4
+ delegate :id, to: :model
3
5
 
4
6
  def self.resource_for(klass)
5
7
  ApiMaker::MemoryStorage.current.resource_for_model(klass)
@@ -11,15 +13,16 @@ class ApiMaker::Serializer
11
13
  ApiMaker::MemoryStorage.current.resource_for_model(klass)
12
14
  end
13
15
 
14
- def initialize(ability: nil, args: {}, model:, select: nil)
15
- @args = args
16
- @model = model
16
+ def initialize(ability: nil, api_maker_args: {}, locals: nil, model:, select: nil)
17
17
  @ability = ability
18
+ @api_maker_args = api_maker_args
19
+ @locals = locals || api_maker_args&.dig(:locals) || {}
20
+ @model = model
18
21
  @select = select
19
22
  end
20
23
 
21
24
  def attributes
22
- ApiMaker::Configuration.profile("attributes") do
25
+ profile("attributes") do
23
26
  result = {}
24
27
  attributes_to_read.each do |attribute, data|
25
28
  if (if_name = data.dig(:args, :if))
@@ -35,14 +38,18 @@ class ApiMaker::Serializer
35
38
  end
36
39
 
37
40
  def attributes_to_read
38
- @attributes_to_read ||= @select || resource.default_select
41
+ @attributes_to_read ||= profile("attributes_to_read") do
42
+ @select || resource.default_select
43
+ end
39
44
  end
40
45
 
41
46
  def attribute_value(attribute)
42
- if resource_instance.respond_to?(attribute)
43
- resource_instance.__send__(attribute)
44
- else
45
- model.__send__(attribute)
47
+ profile("attribute_value #{attribute}") do
48
+ if resource_instance.respond_to?(attribute)
49
+ resource_instance.__send__(attribute)
50
+ else
51
+ model.__send__(attribute)
52
+ end
46
53
  end
47
54
  end
48
55
 
@@ -50,37 +57,61 @@ class ApiMaker::Serializer
50
57
  result.fetch(*args, &blk)
51
58
  end
52
59
 
60
+ def load_ability(ability_name, value)
61
+ @abilities ||= {}
62
+ @abilities[ability_name] = value
63
+ end
64
+
53
65
  def relationships
54
66
  @relationships ||= {}
55
67
  end
56
68
 
57
69
  def resource
58
- @resource ||= ApiMaker::MemoryStorage.current.resource_for_model(model.class)
70
+ @resource ||= profile("resource") do
71
+ ApiMaker::MemoryStorage.current.resource_for_model(model.class)
72
+ end
59
73
  end
60
74
 
61
75
  def resource_instance
62
- @resource_instance ||= resource.new(ability: ability, args: args, model: model)
76
+ @resource_instance ||= profile("resource_instance") do
77
+ resource.new(ability: ability, api_maker_args: api_maker_args, locals: locals, model: model)
78
+ end
63
79
  end
64
80
 
65
81
  def result
66
- @result ||= begin
67
- result = {a: attributes}
68
- result[:r] = @relationships if @relationships # Only include relationships if set
69
- result
70
- end
82
+ result = {a: attributes}
83
+ result[:b] = @abilities if @abilities # Only use b-key if any abilities was loaded
84
+ result[:r] = @relationships if @relationships # Only preload relationships if set
85
+ result
71
86
  end
72
87
 
73
- def as_json(_options = nil)
74
- result
88
+ def as_json(options = nil)
89
+ profile("as_json") do
90
+ if options && options[:result_parser]
91
+ ApiMaker::ResultParser.new(result, ability: ability, api_maker_args: api_maker_args).result
92
+ else
93
+ result
94
+ end
95
+ end
75
96
  end
76
97
 
77
- def to_json(_options = nil)
78
- JSON.generate(as_json)
98
+ def to_json(options = nil)
99
+ profile("to_json") do
100
+ JSON.generate(as_json(options))
101
+ end
79
102
  end
80
103
 
81
104
  def inspect
82
- "<ApiMaker::Serializer id=\"#{model.id}\" model=\"#{model.class.name}\" relationships=\"#{relationships}\">"
105
+ profile("inspect") do
106
+ "<ApiMaker::Serializer id=\"#{model.id}\" model=\"#{model.class.name}\" relationships=\"#{relationships}\">"
107
+ end
83
108
  end
84
109
 
85
110
  alias to_s inspect
111
+
112
+ private
113
+
114
+ def profile(message, &blk)
115
+ ApiMaker::Configuration.profile(-> { "Serializer for #{model.class.name}: #{message}" }, &blk)
116
+ end
86
117
  end
@@ -0,0 +1,14 @@
1
+ module ApiMaker::SpecHelper::BrowserLogs
2
+ def browser_logs
3
+ logs = if browser_firefox?
4
+ []
5
+ else
6
+ chrome_logs
7
+ end
8
+
9
+ @recorded_browser_logs ||= []
10
+ @recorded_browser_logs += logs
11
+
12
+ logs
13
+ end
14
+ end
@@ -0,0 +1,46 @@
1
+ class ApiMaker::SpecHelper::ExecuteCollectionCommand < ApiMaker::ApplicationService
2
+ include RSpec::Mocks::ExampleMethods
3
+
4
+ attr_reader :api_maker_args, :args, :command, :model_class
5
+
6
+ def initialize(ability: nil, api_maker_args: {}, args: {}, command:, fake_controller: nil, model_class:)
7
+ @ability = ability
8
+ @api_maker_args = api_maker_args
9
+ @args = args
10
+ @command = command
11
+ @fake_controller = fake_controller
12
+ @model_class = model_class
13
+ end
14
+
15
+ def perform
16
+ helper_command
17
+ helper.execute!
18
+ succeed! helper_command.result
19
+ end
20
+
21
+ def ability
22
+ @ability ||= ApiMaker::Ability.new(api_maker_args: api_maker_args)
23
+ end
24
+
25
+ def fake_controller
26
+ @fake_controller ||= instance_double(
27
+ ApiMaker::BaseController,
28
+ api_maker_args: api_maker_args,
29
+ current_ability: ability
30
+ )
31
+ end
32
+
33
+ def helper
34
+ @helper ||= ApiMaker::CommandSpecHelper.new(
35
+ collection: model_class,
36
+ command: command,
37
+ controller: fake_controller
38
+ )
39
+ end
40
+
41
+ def helper_command
42
+ @helper_command ||= helper.add_command(
43
+ args: args
44
+ )
45
+ end
46
+ end
@@ -0,0 +1,52 @@
1
+ class ApiMaker::SpecHelper::ExecuteMemberCommand < ApiMaker::ApplicationService
2
+ include RSpec::Mocks::ExampleMethods
3
+
4
+ attr_reader :api_maker_args, :args, :command, :model
5
+
6
+ def initialize(ability: nil, api_maker_args: nil, collection: nil, command:, fake_controller: nil, model:, args: {})
7
+ @ability = ability
8
+ @api_maker_args = api_maker_args
9
+ @args = args
10
+ @collection = collection
11
+ @command = command
12
+ @fake_controller = fake_controller
13
+ @model = model
14
+ end
15
+
16
+ def perform
17
+ helper_command
18
+ helper.execute!
19
+ succeed! helper_command.result
20
+ end
21
+
22
+ def ability
23
+ @ability ||= ApiMaker::Ability.new(api_maker_args: api_maker_args)
24
+ end
25
+
26
+ def collection
27
+ @collection ||= model.class.where(id: model.id)
28
+ end
29
+
30
+ def fake_controller
31
+ @fake_controller ||= instance_double(
32
+ ApiMaker::BaseController,
33
+ api_maker_args: api_maker_args,
34
+ current_ability: ability
35
+ )
36
+ end
37
+
38
+ def helper
39
+ @helper ||= ApiMaker::CommandSpecHelper.new(
40
+ collection: collection,
41
+ command: command,
42
+ controller: fake_controller
43
+ )
44
+ end
45
+
46
+ def helper_command
47
+ @helper_command ||= helper.add_command(
48
+ primary_key: model.id,
49
+ args: args
50
+ )
51
+ end
52
+ end
@@ -0,0 +1,18 @@
1
+ module ApiMaker::SpecHelper::ExpectNoBrowserErrors
2
+ def expect_no_browser_errors
3
+ logs = browser_logs
4
+ .map(&:to_s)
5
+ .reject { |log| log.include?("Warning: Can't perform a React state update on an unmounted component.") }
6
+ .reject { |log| log.include?("DEBUG: ") }
7
+ .join("\n")
8
+
9
+ expect_no_browser_window_errors
10
+ return if logs.blank? || logs.exclude?("SEVERE ")
11
+
12
+ # Lets try one more time - just in case browser window error got registered meanwhile
13
+ sleep 0.4
14
+ expect_no_browser_window_errors
15
+
16
+ raise logs
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module ApiMaker::SpecHelper::WaitForExpect
2
+ # Waits for an expect to not fail - this is great because it whines
3
+ def wait_for_expect(*args)
4
+ last_error = nil
5
+
6
+ begin
7
+ wait_for_browser(*args) do
8
+ yield
9
+ true
10
+ rescue RSpec::Expectations::ExpectationNotMetError => e
11
+ last_error = e
12
+ false
13
+ end
14
+ rescue WaitUtil::TimeoutError => e
15
+ raise last_error if last_error
16
+
17
+ raise e
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module ApiMaker::SpecHelper::WaitForFlashMessage
2
+ def wait_for_flash_message(expected_message, delay_sec: 0.2, timeout_sec: 6)
3
+ received_messages = []
4
+
5
+ WaitUtil.wait_for_condition("wait for flash message", timeout_sec: timeout_sec, delay_sec: delay_sec) do
6
+ expect_no_browser_errors
7
+ current_message = flash_message_text
8
+ received_messages << current_message
9
+
10
+ if current_message.is_a?(Array)
11
+ current_message.include?(expected_message)
12
+ else
13
+ current_message == expected_message
14
+ end
15
+ end
16
+
17
+ expect_no_browser_errors
18
+ rescue WaitUtil::TimeoutError
19
+ expect(received_messages.uniq.reject(&:blank?)).to eq include expected_message
20
+ end
21
+ end
@@ -1,11 +1,36 @@
1
- module ApiMaker::SpecHelper
1
+ require "waitutil"
2
+
3
+ module ApiMaker::SpecHelper # rubocop:disable Metrics/ModuleLength
4
+ autoload :ExecuteCollectionCommand, "#{__dir__}/spec_helper/execute_collection_command"
5
+ autoload :ExecuteMemberCommand, "#{__dir__}/spec_helper/execute_member_command"
6
+
7
+ require_relative "spec_helper/browser_logs"
8
+ require_relative "spec_helper/expect_no_browser_errors"
9
+ require_relative "spec_helper/wait_for_expect"
10
+ require_relative "spec_helper/wait_for_flash_message"
11
+ include BrowserLogs
12
+ include ExpectNoBrowserErrors
13
+ include WaitForExpect
14
+ include WaitForFlashMessage
15
+
16
+ class JavaScriptError < RuntimeError; end
17
+ class SelectorNotFoundError < RuntimeError; end
18
+ class SelectorFoundError < RuntimeError; end
19
+
20
+ def browser_firefox?
21
+ capabilities = page.driver.browser.try(:capabilities)
22
+ capabilities && capabilities[:browser_name] == "firefox"
23
+ end
24
+
2
25
  def chrome_logs
3
26
  page.driver.browser.manage.logs.get(:browser)
4
27
  end
5
28
 
6
- def expect_no_chrome_window_errors
7
- sleep 1
29
+ def confirm_accept
30
+ page.driver.browser.switch_to.alert.accept
31
+ end
8
32
 
33
+ def expect_no_browser_window_errors
9
34
  errors = execute_script("if (window.errorLogger) { return window.errorLogger.getErrors() }")
10
35
  return if !errors.is_a?(Array) || errors.empty?
11
36
 
@@ -15,81 +40,112 @@ module ApiMaker::SpecHelper
15
40
  custom_trace += last_error.fetch("backtrace") if last_error["backtrace"].is_a?(Array)
16
41
  custom_trace += caller
17
42
 
18
- error = RuntimeError.new("#{last_error["errorClass"]}: #{last_error["message"]}")
43
+ error = JavaScriptError.new("#{last_error["errorClass"]}: #{last_error["message"]}")
19
44
  error.set_backtrace(custom_trace)
20
45
 
21
46
  raise error
22
47
  end
23
48
 
24
- def expect_no_chrome_errors
25
- logs = chrome_logs.map(&:to_s)
26
- logs = logs.reject { |log| log.include?("Warning: Can't perform a React state update on an unmounted component.") }
27
- return if !logs || !logs.join("\n").include?("SEVERE ")
49
+ def expect_no_errors
50
+ expect_no_flash_errors
51
+ expect_no_browser_errors
52
+ end
28
53
 
29
- expect_no_chrome_window_errors
54
+ def expect_to_be_able_to(ability, model, permissions)
55
+ permissions.each do |permission|
56
+ # Test access through 'can?'
57
+ expect(ability).to be_able_to permission, model
30
58
 
31
- puts logs.join("\n")
32
- expect(logs).to eq nil
59
+ # Test access through 'accessible_by'
60
+ if model.is_a?(ActiveRecord::Base) && model.persisted?
61
+ readable_models = model.class.where(id: model).accessible_by(ability, permission)
62
+ expect(readable_models).to eq [model]
63
+ end
64
+ end
33
65
  end
34
66
 
35
- def expect_no_errors
36
- expect_no_flash_errors
37
- expect_no_chrome_errors
67
+ def expect_not_to_be_able_to(ability, model, permissions)
68
+ permissions.each do |permission|
69
+ # Test access through 'can?'
70
+ expect(ability).not_to be_able_to permission, model
71
+
72
+ # Test access through 'accessible_by'
73
+ if model.is_a?(ActiveRecord::Base) && model.persisted?
74
+ readable_models = model.class.where(id: model).accessible_by(ability, permission)
75
+ expect(readable_models).to be_empty
76
+ end
77
+ end
38
78
  end
39
79
 
40
80
  def js_fill_in(element_id, with:)
41
81
  page.execute_script("document.querySelector(#{element_id.to_json}).value = #{with.to_json}")
42
82
  end
43
83
 
84
+ def model_row_selector(model)
85
+ ".#{model.model_name.singular.dasherize}-row[data-model-id='#{model.id}']"
86
+ end
87
+
44
88
  def pretty_html
45
89
  require "htmlbeautifier"
46
90
  HtmlBeautifier.beautify(page.html)
47
91
  end
48
92
 
93
+ def recorded_browser_logs
94
+ @recorded_browser_logs || []
95
+ end
96
+
49
97
  def reset_indexeddb
50
- execute_script "
51
- indexedDB.databases().then(function(databases) {
52
- var promises = []
53
- for(var database of databases) {
54
- promises.push(indexedDB.deleteDatabase(database.name))
55
- }
56
-
57
- Promise.all(promises).then(function() {
58
- console.error('All databases was deleted')
59
- })
60
- })
61
- "
62
-
63
- wait_for_condition do
64
- logs_text = chrome_logs.map(&:message).join("\n")
65
- logs_text.include?("\"All databases was deleted\"")
66
- end
98
+ ApiMaker::ResetIndexedDbService.execute!(context: self)
99
+ end
100
+
101
+ def wait_for_action_cable_to_connect
102
+ sleep 0.5
103
+ end
104
+
105
+ def wait_for_and_find(selector, *args)
106
+ element = find(selector, *args)
107
+ expect_no_browser_errors
108
+ element
109
+ rescue Capybara::ElementNotFound => e
110
+ expect_no_browser_errors
111
+ raise ApiMaker::SpecHelper::SelectorNotFoundError, e.message
67
112
  end
68
113
 
69
- def wait_for_chrome(delay_sec: 0.5, timeout_sec: 6)
70
- WaitUtil.wait_for_condition("wait for chrome", timeout_sec: timeout_sec, delay_sec: delay_sec) do
71
- expect_no_chrome_errors
114
+ def wait_for_attribute_row(attribute: nil, identifier: nil, label: nil, value: nil, **opts)
115
+ raise "No attribute or identifier given" if !attribute && !identifier
116
+ raise "No label or value was given" if !label && !value
117
+
118
+ tr_selector = ".component-api-maker-attribute-row"
119
+ tr_selector << "[data-attribute='#{attribute.camelize(:lower)}']" if attribute
120
+ tr_selector << "[data-identifier='#{identifier.camelize}']" if identifier
121
+
122
+ wait_for_selector "#{tr_selector} > .attribute-row-label", exact_text: label, **opts if label
123
+ wait_for_selector "#{tr_selector} > .attribute-row-value", exact_text: value, **opts if value
124
+ end
125
+
126
+ def wait_for_browser(delay_sec: 0.2, message: "wait for browser", timeout_sec: 6)
127
+ WaitUtil.wait_for_condition(message, timeout_sec: timeout_sec, delay_sec: delay_sec) do
128
+ expect_no_browser_errors
72
129
  yield
73
130
  end
74
131
  end
75
132
 
76
- def wait_for_flash_message(expected_message, delay_sec: 0.5, timeout_sec: 10)
77
- received_messages = []
133
+ def wait_for_path(expected_path, **args)
134
+ args[:ignore_query] = true unless args.key?(:ignore_query)
78
135
 
79
- begin
80
- WaitUtil.wait_for_condition("wait for flash message", timeout_sec: timeout_sec, delay_sec: delay_sec) do
81
- expect_no_chrome_errors
82
- current_message = flash_message_text
83
- received_messages << current_message
84
- current_message == expected_message
85
- end
86
- rescue WaitUtil::TimeoutError
87
- expect(received_messages.uniq.reject(&:blank?)).to eq include expected_message
88
- end
136
+ expect(page).to have_current_path(expected_path, args)
137
+ expect_no_browser_errors
138
+ rescue RSpec::Expectations::ExpectationNotMetError => e
139
+ expect_no_browser_errors
140
+ raise e
89
141
  end
90
142
 
91
- def wait_for_selector(selector)
92
- wait_for_chrome { page.has_selector?(selector) }
143
+ def wait_for_selector(selector, *args)
144
+ expect(page).to have_selector selector, *args
145
+ expect_no_browser_errors
146
+ rescue RSpec::Expectations::ExpectationNotMetError => e
147
+ expect_no_browser_errors
148
+ raise ApiMaker::SpecHelper::SelectorNotFoundError, e.message
93
149
  end
94
150
 
95
151
  def wait_for_selectors(*selectors)
@@ -97,4 +153,12 @@ module ApiMaker::SpecHelper
97
153
  wait_for_selector(selector)
98
154
  end
99
155
  end
156
+
157
+ def wait_for_no_selector(selector, *args)
158
+ expect(page).to have_no_selector selector, *args
159
+ expect_no_browser_errors
160
+ rescue RSpec::Expectations::ExpectationNotMetError => e
161
+ expect_no_browser_errors
162
+ raise ApiMaker::SpecHelper::SelectorFoundError, e.message
163
+ end
100
164
  end
@@ -1,3 +1,3 @@
1
1
  module ApiMaker
2
- VERSION = "0.0.1".freeze
2
+ VERSION = "0.0.2".freeze
3
3
  end
data/lib/api_maker.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require "api_maker/engine"
2
2
 
3
+ require "active_record_better_dependent_error_messages"
3
4
  require "active_record_query_fixer"
5
+ require "cancancan"
4
6
  require "ransack"
5
7
  require "service_pattern"
6
8
 
@@ -11,22 +13,24 @@ module ApiMaker
11
13
 
12
14
  autoload :Ability, "#{dir}/ability"
13
15
  autoload :AbilityLoader, "#{dir}/ability_loader"
14
- autoload :BaseCommand, "#{dir}/base_command"
16
+ autoload :BaseCollectionInstance, "#{dir}/base_collection_instance"
15
17
  autoload :BaseResource, "#{dir}/base_resource"
18
+ autoload :BaseService, "#{dir}/base_service"
16
19
  autoload :CollectionSerializer, "#{dir}/collection_serializer"
17
20
  autoload :CommandSpecHelper, "#{dir}/command_spec_helper"
18
21
  autoload :Configuration, "#{dir}/configuration"
22
+ autoload :ExpectToBeAbleToHelper, "#{dir}/expect_to_able_to_helper"
19
23
  autoload :IndividualCommand, "#{dir}/individual_command"
20
24
  autoload :Loader, "#{dir}/loader"
21
25
  autoload :MemoryStorage, "#{dir}/memory_storage"
22
26
  autoload :ModelExtensions, "#{dir}/model_extensions"
23
27
  autoload :PermittedParamsArgument, "#{dir}/permitted_params_argument"
24
28
  autoload :Preloader, "#{dir}/preloader"
29
+ autoload :PreloaderBase, "#{dir}/preloader_base"
25
30
  autoload :PreloaderBelongsTo, "#{dir}/preloader_belongs_to"
26
31
  autoload :PreloaderHasMany, "#{dir}/preloader_has_many"
27
32
  autoload :PreloaderHasOne, "#{dir}/preloader_has_one"
28
- autoload :PreloaderThrough, "#{dir}/preloader_through"
29
- autoload :RelationshipIncluder, "#{dir}/relationship_includer"
33
+ autoload :RelationshipPreloader, "#{dir}/relationship_preloader"
30
34
  autoload :ResourceRouting, "#{dir}/resource_routing"
31
35
  autoload :ResultParser, "#{dir}/result_parser"
32
36
  autoload :Serializer, "#{dir}/serializer"