formatic 0.5.0 → 0.6.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +16 -3
- data/app/components/formatic/file.rb +27 -20
- data/lib/formatic/testing/capybara.rb +8 -82
- data/lib/formatic/testing/search.rb +87 -0
- data/lib/formatic/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d41f61aacdbc9679d1e63ff82441ac6008ae35f53a8b5425986e3bac53baeb0d
|
|
4
|
+
data.tar.gz: 2974cac3f7d742dc001b45a2c5cdfe81f71e4e2cfd24d6052b4d563a790423c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4da00730403d13520acf91dd762fe73e9a007ba6ba4146aa96cfb40d9e18ee71e4d45eb54569fab08a87df55838ee6c6302b1881c6b4a8eaa3376b10cf91462f
|
|
7
|
+
data.tar.gz: 7d37f9de2bb33f902fb85390aa8bcdc0f1500131196cf55107eeaaa5df94cd78e22bb34bc88cadaae41ac20457eb3d74d34053989da08d457d7e2a04f99cfd51
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## [0.6.0] 2026-10-26
|
|
2
|
+
|
|
3
|
+
- Include `Formatic::Testing::Capybara::Search` in a Capybara RackTest class to call `search 'Acme Corp', from: 'Customer'`
|
|
4
|
+
|
|
5
|
+
## [0.5.1] 2026-10-26
|
|
6
|
+
|
|
7
|
+
- Rack tests call `Formatic::Testing::Search.call` instead of including a Capybara module
|
|
8
|
+
|
|
1
9
|
## [0.5.0] 2026-10-26
|
|
2
10
|
|
|
3
11
|
- Add `Formatic::Search`, a JSON-backed search field
|
data/README.md
CHANGED
|
@@ -29,11 +29,24 @@ after a successful response, even when there are no matches.
|
|
|
29
29
|
url: admin_commanders_path(format: :json))
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
For Rack tests,
|
|
33
|
-
|
|
34
|
-
reads the field's URL and parameter names, requests matching JSON directly, and
|
|
32
|
+
For Rack tests, call `Formatic::Testing::Search.call(page:, query: 'Bob', from: 'Users')`.
|
|
33
|
+
It reads the field's URL and parameter names, requests matching JSON directly, and
|
|
35
34
|
fills the hidden input with a result returned by that endpoint.
|
|
36
35
|
|
|
36
|
+
Capybara RackTest test classes can include the `search` helper instead:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
require 'formatic/testing/capybara'
|
|
40
|
+
|
|
41
|
+
class CustomersTest < ActionDispatch::SystemTestCase
|
|
42
|
+
include Formatic::Testing::Capybara::Search
|
|
43
|
+
|
|
44
|
+
test 'select a customer' do
|
|
45
|
+
search 'Acme Corp', from: 'Customer'
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
```
|
|
49
|
+
|
|
37
50
|
## Installation
|
|
38
51
|
|
|
39
52
|
Install the gem and add to the application's Gemfile by executing:
|
|
@@ -49,18 +49,22 @@ module Formatic
|
|
|
49
49
|
private
|
|
50
50
|
|
|
51
51
|
def validate_attribute_name!
|
|
52
|
+
return unless validate_attribute_name?
|
|
53
|
+
return if f.object.respond_to?(attribute_name)
|
|
54
|
+
|
|
55
|
+
raise ArgumentError,
|
|
56
|
+
"attribute `#{attribute_name}` does not exist on #{f.object.class}."
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def validate_attribute_name?
|
|
52
60
|
# A manually passed value means `attribute_name` is just a param name,
|
|
53
61
|
# not necessarily a method on the model.
|
|
54
|
-
return if manual_value != :_fetch_from_record
|
|
55
|
-
return if f.nil? || f.object.nil?
|
|
62
|
+
return false if manual_value != :_fetch_from_record
|
|
63
|
+
return false if f.nil? || f.object.nil?
|
|
56
64
|
|
|
57
65
|
# Only enforce this for real ActiveModel/ActiveRecord records.
|
|
58
66
|
# Custom objects (e.g. slugs) are not expected to respond to it.
|
|
59
|
-
|
|
60
|
-
return if f.object.respond_to?(attribute_name)
|
|
61
|
-
|
|
62
|
-
raise ArgumentError,
|
|
63
|
-
"attribute `#{attribute_name}` does not exist on #{f.object.class}."
|
|
67
|
+
f.object.respond_to?(:model_name)
|
|
64
68
|
end
|
|
65
69
|
|
|
66
70
|
def attachments
|
|
@@ -77,19 +81,22 @@ module Formatic
|
|
|
77
81
|
# Filepond doesn't work well with invalid form submits anyway.
|
|
78
82
|
return if multiple
|
|
79
83
|
|
|
80
|
-
attachments.map
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
attachments.map { |attachment| entry_json(attachment) }.to_json
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def entry_json(attachment)
|
|
88
|
+
{
|
|
89
|
+
source: attachment.signed_id,
|
|
90
|
+
options: { type: 'local', file: file_json(attachment) }
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def file_json(attachment)
|
|
95
|
+
{
|
|
96
|
+
name: attachment.filename,
|
|
97
|
+
size: attachment.byte_size,
|
|
98
|
+
type: attachment.content_type
|
|
99
|
+
}
|
|
93
100
|
end
|
|
94
101
|
|
|
95
102
|
# The currently attached file, if there is exactly one.
|
|
@@ -1,92 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
require 'calls'
|
|
3
|
+
require_relative 'search'
|
|
5
4
|
|
|
6
5
|
module Formatic
|
|
7
6
|
module Testing
|
|
8
7
|
module Capybara
|
|
8
|
+
# Include in a Capybara RackTest test class to select Formatic search results.
|
|
9
|
+
#
|
|
10
|
+
# include Formatic::Testing::Capybara::Search
|
|
11
|
+
#
|
|
12
|
+
# search 'Acme Corp', from: 'Customer'
|
|
9
13
|
module Search
|
|
10
|
-
def
|
|
11
|
-
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
class SearchSelector
|
|
15
|
-
include Calls
|
|
16
|
-
|
|
17
|
-
class SelectionError < ::StandardError; end
|
|
18
|
-
|
|
19
|
-
option :page
|
|
20
|
-
option :query
|
|
21
|
-
option :from
|
|
22
|
-
|
|
23
|
-
def call
|
|
24
|
-
ensure_rack_test_driver!
|
|
25
|
-
query_field = page.find_field(from)
|
|
26
|
-
component = search_component(query_field)
|
|
27
|
-
record = matching_record(search_records(component), query)
|
|
28
|
-
|
|
29
|
-
raise SelectionError, "No result matching #{query.inspect} from #{from.inspect}" unless record
|
|
30
|
-
|
|
31
|
-
apply_selection(component, query_field, record)
|
|
32
|
-
record
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def ensure_rack_test_driver!
|
|
38
|
-
require 'capybara/rack_test/driver'
|
|
39
|
-
return if page.driver.is_a?(::Capybara::RackTest::Driver)
|
|
40
|
-
|
|
41
|
-
raise ArgumentError, 'select_record_from_search requires Capybara RackTest'
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def search_component(query_field)
|
|
45
|
-
query_field.find(:xpath, 'ancestor::div[contains(@class, "js-formatic-search")][1]')
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def search_records(component)
|
|
49
|
-
response = search_request(configured_value(component, 'resource'), search_params(component))
|
|
50
|
-
|
|
51
|
-
raise SelectionError, "Search for #{from.inspect} returned HTTP #{response.status}" unless response.ok?
|
|
52
|
-
|
|
53
|
-
JSON.parse(response.body).fetch('records')
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def search_params(component)
|
|
57
|
-
{
|
|
58
|
-
query: query,
|
|
59
|
-
record_name: configured_value(component, 'record-name'),
|
|
60
|
-
attribute_name: configured_value(component, 'attribute-name'),
|
|
61
|
-
exclude_ids: configured_value(component, 'exclude-ids')
|
|
62
|
-
}
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def configured_value(component, class_suffix)
|
|
66
|
-
component.find(".js-formatic-search__#{class_suffix}", visible: :all).value
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def search_request(url, params)
|
|
70
|
-
browser = page.driver.browser
|
|
71
|
-
browser.current_session.restore_state do
|
|
72
|
-
browser.get(url, params, 'HTTP_ACCEPT' => 'application/json')
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def matching_record(records, query)
|
|
77
|
-
records.find do |record|
|
|
78
|
-
text = record.fetch('choice').gsub(/<[^>]*>/, ' ')
|
|
79
|
-
text.downcase.include?(query.downcase)
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def apply_selection(component, query_field, record)
|
|
84
|
-
component.find('.js-formatic-search__value', visible: :all).set(record.fetch('id').to_s)
|
|
85
|
-
selected = component.find('.js-formatic-search__selected', visible: :all)
|
|
86
|
-
selected.native.inner_html = record.fetch('selected')
|
|
87
|
-
component.find('.js-formatic-search__results', visible: :all).native.inner_html = ''
|
|
88
|
-
query_field.set('')
|
|
89
|
-
end
|
|
14
|
+
def search(query, from:)
|
|
15
|
+
::Formatic::Testing::Search.call(page:, query:, from:)
|
|
90
16
|
end
|
|
91
17
|
end
|
|
92
18
|
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'calls'
|
|
5
|
+
|
|
6
|
+
module Formatic
|
|
7
|
+
module Testing
|
|
8
|
+
# Selects a Formatic search result in a Rack test.
|
|
9
|
+
class Search
|
|
10
|
+
include Calls
|
|
11
|
+
|
|
12
|
+
class SelectionError < ::StandardError; end
|
|
13
|
+
|
|
14
|
+
option :page
|
|
15
|
+
option :query
|
|
16
|
+
option :from
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
ensure_rack_test_driver!
|
|
20
|
+
query_field = page.find_field(from)
|
|
21
|
+
component = search_component(query_field)
|
|
22
|
+
record = matching_record(search_records(component), query)
|
|
23
|
+
|
|
24
|
+
raise SelectionError, "No result matching #{query.inspect} from #{from.inspect}" unless record
|
|
25
|
+
|
|
26
|
+
apply_selection(component, query_field, record)
|
|
27
|
+
record
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def ensure_rack_test_driver!
|
|
33
|
+
require 'capybara/rack_test/driver'
|
|
34
|
+
return if page.driver.is_a?(::Capybara::RackTest::Driver)
|
|
35
|
+
|
|
36
|
+
raise ArgumentError, 'Formatic::Testing::Search requires Capybara RackTest'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def search_component(query_field)
|
|
40
|
+
query_field.find(:xpath, 'ancestor::div[contains(@class, "js-formatic-search")][1]')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def search_records(component)
|
|
44
|
+
response = search_request(configured_value(component, 'url'), search_params(component))
|
|
45
|
+
|
|
46
|
+
raise SelectionError, "Search for #{from.inspect} returned HTTP #{response.status}" unless response.ok?
|
|
47
|
+
|
|
48
|
+
JSON.parse(response.body).fetch('records')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def search_params(component)
|
|
52
|
+
{
|
|
53
|
+
query: query,
|
|
54
|
+
record_name: configured_value(component, 'record-name'),
|
|
55
|
+
attribute_name: configured_value(component, 'attribute-name'),
|
|
56
|
+
exclude_ids: configured_value(component, 'exclude-ids')
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def configured_value(component, class_suffix)
|
|
61
|
+
component.find(".js-formatic-search__#{class_suffix}", visible: :all).value
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def search_request(url, params)
|
|
65
|
+
browser = page.driver.browser
|
|
66
|
+
browser.current_session.restore_state do
|
|
67
|
+
browser.get(url, params, 'HTTP_ACCEPT' => 'application/json')
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def matching_record(records, query)
|
|
72
|
+
records.find do |record|
|
|
73
|
+
text = record.fetch('choice').gsub(/<[^>]*>/, ' ')
|
|
74
|
+
text.downcase.include?(query.downcase)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def apply_selection(component, query_field, record)
|
|
79
|
+
component.find('.js-formatic-search__value', visible: :all).set(record.fetch('id').to_s)
|
|
80
|
+
selected = component.find('.js-formatic-search__selected', visible: :all)
|
|
81
|
+
selected.native.inner_html = record.fetch('selected')
|
|
82
|
+
component.find('.js-formatic-search__results', visible: :all).native.inner_html = ''
|
|
83
|
+
query_field.set('')
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
data/lib/formatic/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: formatic
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- halo
|
|
@@ -218,6 +218,7 @@ files:
|
|
|
218
218
|
- lib/formatic/templates/select.rb
|
|
219
219
|
- lib/formatic/templates/wrapper.rb
|
|
220
220
|
- lib/formatic/testing/capybara.rb
|
|
221
|
+
- lib/formatic/testing/search.rb
|
|
221
222
|
- lib/formatic/version.rb
|
|
222
223
|
- lib/formatic/vscode_snippets.rb
|
|
223
224
|
- lib/formatic/wrappers/alternative_attribute_name.rb
|