with_form 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9e706ed3dfe301d30053e531b040f1588ae35ca430f4c1de408bd01bd777e8e
4
- data.tar.gz: dfbc4ba02b11b52f69a852e1b66b226ce10ef86a7e6bf075264b70ed4e808cbb
3
+ metadata.gz: 7a78f39980ba03659bd61b33c94e65e8987dc590647ba8870271141a879eafca
4
+ data.tar.gz: ad43310ff4fd4a42ddb3f154af5c9e799f1a25c82dafdd46c9d18a6f41f48304
5
5
  SHA512:
6
- metadata.gz: 2fc1f5efb683e554791d22d0ec68ef467fffb768a9b9c1c96100545bd882715147757f91715d534be2eb9fa9dbc18ad3c8f9fe8785cd891e722d5a80450d3a91
7
- data.tar.gz: 21d93934220c9b60dfa9aa1814b8f72121001f2e11f43b236634fd722e8c967cc03214f7208458244897b81845362b68bf8fe02823f9c19a48e7390b65174b40
6
+ metadata.gz: 2a300ce3b20953400496bc7b7cf09e0bb9206870526c8d63cfb6987db7537ffe702788cd1543ad9eb28f83b99ac44bb39063b628be3677eac73069874a0bdbd6
7
+ data.tar.gz: b596e4300be6ac53de36e9e81e85514722d1763f3c877ed82aa1b5b38a04adb8d02995d625d89eb800a07938f42343a76d565aa62c0523e338e16845aa675e95
data/README.md CHANGED
@@ -7,6 +7,8 @@ Your System Test's counterpart to `form_with`
7
7
  Leverage Rails-generated `<label>` values to submit `<form>` elements in System
8
8
  Tests.
9
9
 
10
+ ### The `with_form` test helper
11
+
10
12
  To add coverage to a form's fields that are generated by ActionView's
11
13
  `form_with` helper, fill them using `with_form`:
12
14
 
@@ -60,6 +62,29 @@ The `with_form` helper method accepts two styles of options:
60
62
 
61
63
  * `scope:` - the internationalization scope key to use when translating Capybara's
62
64
  locator values
65
+
66
+ When submitting a `<form>` through a call to `form.click_button`, you can pass
67
+ an `action` as the translation scope. A `with_form(scope:)` call will default
68
+ to the `submit` key when one is not specified. For instance:
69
+
70
+ ```ruby
71
+ form_with(scope: :post) do |form|
72
+ form.click_button
73
+ end
74
+ ```
75
+
76
+ This call will search for an `<input type="text">` or `<button>` whose `value`
77
+ or text content is the String translated by the `helpers.submit.post.submit`
78
+ key.
79
+
80
+ That action can be overridden:
81
+
82
+ ```ruby
83
+ form_with(scope: :post) do |form|
84
+ form.click_button :create
85
+ end
86
+ ```
87
+
63
88
  * `model:` - an instance of an `ActiveModel::Model` or `ActiveRecord::Base` to
64
89
  be used to translate Capybara's locator values, and to populate the fields
65
90
  with an attribute's value.
@@ -97,9 +122,65 @@ The `with_form` helper method accepts two styles of options:
97
122
  `"New Title"`) will take precedence over the `post.title` attribute's value
98
123
  (in this case, `"Old Title"`).
99
124
 
125
+ When submitting a `<form>` through a call to `form.click_button`, you can pass
126
+ an `action` as the translation scope. A `with_form(model:)` call will
127
+ determine the translation key based on [the `model`
128
+ argument's persistence state][persisted].
129
+
130
+ When a `model` instance is a new record, the key will use `create`. For
131
+ instance:
132
+
133
+ ```ruby
134
+ post = Post.new
135
+
136
+ form_with(model: post) do |form|
137
+ form.click_button
138
+ end
139
+ ```
140
+
141
+ This call will search for an `<input type="text">` or `<button>` whose `value`
142
+ or text content is the String translated by the `helpers.submit.post.create`
143
+ key.
144
+
145
+ That action can be overridden:
146
+
147
+ ```ruby
148
+ post = Post.new
149
+
150
+ form_with(model: post) do |form|
151
+ form.click_button :submit
152
+ end
153
+ ```
154
+
155
+ When a `model` instance is an existing persisted record, the key will use
156
+ `update`. For instance:
157
+
158
+ ```ruby
159
+ post = Post.last
160
+
161
+ form_with(model: post) do |form|
162
+ form.click_button
163
+ end
164
+ ```
165
+
166
+ This call will search for an `<input type="text">` or `<button>` whose `value`
167
+ or text content is the String translated by the `helpers.submit.post.update`
168
+ key.
169
+
170
+ That action can be overridden:
171
+
172
+ ```ruby
173
+ post = Post.last
174
+
175
+ form_with(model: post) do |form|
176
+ form.click_button :submit
177
+ end
178
+ ```
179
+
100
180
  [label]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label
101
181
  [input]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
102
182
  [textarea]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea
183
+ [persisted]: https://api.rubyonrails.org/classes/ActiveModel/Model.html#method-i-persisted-3F
103
184
 
104
185
  With the exception of `#click_link` and `#click_link_or_button`, the argument
105
186
  yielded to `with_form` supports [all helper methods made available by
@@ -118,6 +199,117 @@ Those include:
118
199
 
119
200
  [actions]: https://www.rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Actions
120
201
 
202
+ #### ActionText `rich_text_area` support
203
+
204
+ When [`ActionText`][actiontext] is available, `with_form` provides a
205
+ `#fill_in_rich_text_area` helper method.
206
+
207
+ The current implementation is inspired by
208
+ `ActionText::SystemTestHelper#fill_in_rich_text_area` that is currently declared
209
+ on the current [`rails@master` branch][fill_in_rich_text_area].
210
+
211
+ ```ruby
212
+ class UserInteractsWithRichTextAreasTest < ApplicationSystemTestCase
213
+ include WithForm::TestHelpers
214
+
215
+ test "makes a post with a scope: argument" do
216
+ visit new_post_path
217
+ with_form(scope: :post) do |form|
218
+ form.fill_in_rich_text_area :body, with: "My First Post"
219
+ form.click_button
220
+ end
221
+
222
+ assert_text "My First Post"
223
+ end
224
+
225
+ test "user makes a post with a model: argument" do
226
+ post = Post.new(body: "My First Post")
227
+
228
+ visit new_post_path
229
+ with_form(model: post) do |form|
230
+ form.fill_in_rich_text_area :body
231
+ form.click_button
232
+ end
233
+
234
+ assert_text "My First Post"
235
+ end
236
+ end
237
+ ```
238
+
239
+ There is a current limitation in how the [`rails@master`-inspired
240
+ `#fill_in_rich_text_area` implementation][fill_in_rich_text_area] resolves the
241
+ `locator` argument. Since the underlying `<trix-editor>` element is not a
242
+ default field provided by the browser, focussing on its corresponding `<label>`
243
+ element won't focus the `<trix-editor>`. To resolve that shortcoming, the
244
+ `#fill_in_rich_text_area` uses the `<trix-editor aria-label="...">` attribute as
245
+ the label text.
246
+
247
+ This is a helpful, but incomplete solution to the problem. This requires that
248
+ instead of declaring a `<label for="my_rich_text_field">` element
249
+ referencing the `<trix-editor id="my_rich_text_field">` element, the `<label>`
250
+ element's text (or rather, the text that _would be_ in the `<label>` element)
251
+ must be passed to the `<trix-editor aria-label="...">` attribute.
252
+
253
+ For example:
254
+
255
+ ```html+erb
256
+ <%= form_with(model: Post.new) do |form %>
257
+ <%= form.label :my_rich_text_field %>
258
+ <%= form.rich_text_area :my_rich_text_field, "aria-label": translate(:my_rich_text_field, scope: "helpers.label.post") %>
259
+ <% end %>
260
+ ```
261
+
262
+ [actiontext]: https://edgeguides.rubyonrails.org/action_text_overview.html#examples
263
+ [fill_in_rich_text_area]: https://github.com/rails/rails/blob/339be65d669c83fd4c64541a9e82086dc5e64682/actiontext/lib/action_text/system_test_helper.rb
264
+
265
+ ### The `label` and `submit` test helpers
266
+
267
+ While `with_form` can simplify `<form>` element interactions with multiple
268
+ steps, there are times when a single line of instructions is more convenient.
269
+
270
+ Behind the scenes, `with_form` utilize the `#label` and `#submit` helper
271
+ methods to translate `<label>` and `<button>` text, along with `<input
272
+ type="submit">` values.
273
+
274
+ To put the same helpers to use within your test, include the
275
+ `WithForm::TranslationHelpers` module and invoke either:
276
+
277
+ * `label(model_name, attribute)`
278
+
279
+ * `submit(model_name, action = :submit)`
280
+
281
+ For example when clicking an `<input type="checkbox">` labelled by a translation
282
+ declared at `helpers.label.session.ready`:
283
+
284
+ ```ruby
285
+ class UserInteractsWithFormsTest < ApplicationSystemTestCase
286
+ include WithForm::TranslationHelpers
287
+
288
+ test "user ticks a box" do
289
+ visit new_session_path
290
+ check label(:session, :ready)
291
+
292
+ assert_text "We're glad you're ready, user@example.com."
293
+ end
294
+ end
295
+ ```
296
+
297
+ Or, to destroy a Post by clicking a button labelled by a translation declared at
298
+ `helpers.submit.post.destroy`:
299
+
300
+ ```ruby
301
+ class UserInteractsWithFormsTest < ApplicationSystemTestCase
302
+ include WithForm::TranslationHelpers
303
+
304
+ test "user deletes a post" do
305
+ visit new_post_path
306
+ click_on submit(:post, :destroy)
307
+
308
+ assert_text "Deleted Post."
309
+ end
310
+ end
311
+ ```
312
+
121
313
  ## Installation
122
314
 
123
315
  Add this line to your application's Gemfile:
@@ -186,6 +378,10 @@ I've used the [`formulaic` gem][formulaic] before. How is this gem different?
186
378
  [form_with]: https://guides.rubyonrails.org/form_helpers.html#dealing-with-basic-forms
187
379
  [rails-i18n]: https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-label
188
380
 
381
+ ## Contributing
382
+
383
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
384
+
189
385
  ## License
190
386
 
191
387
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,4 +1,4 @@
1
- class CreateWidgetRecords < ActiveRecord::Migration[6.0]
1
+ class CreateWidgetRecords < ActiveRecord::Migration[Rails.version.to_f]
2
2
  def change
3
3
  create_table :widget_records do |t|
4
4
  t.text :text_field
@@ -1,4 +1,22 @@
1
1
  module WithForm
2
2
  class Engine < ::Rails::Engine
3
+ ActiveSupport.on_load :action_dispatch_system_test_case do
4
+ Capybara.add_selector :rich_text_area do
5
+ label "rich-text area"
6
+ xpath do |locator|
7
+ if locator.nil?
8
+ XPath.descendant(:"trix-editor")
9
+ else
10
+ input_located_by_name = XPath.anywhere(:input).where(XPath.attr(:name) == locator).attr(:id)
11
+
12
+ XPath.descendant(:"trix-editor").where \
13
+ XPath.attr(:id).equals(locator) |
14
+ XPath.attr(:placeholder).equals(locator) |
15
+ XPath.attr(:"aria-label").equals(locator) |
16
+ XPath.attr(:input).equals(input_located_by_name)
17
+ end
18
+ end
19
+ end
20
+ end
3
21
  end
4
22
  end
@@ -15,6 +15,14 @@ module WithForm
15
15
  )
16
16
  end
17
17
 
18
+ def fill_in_rich_text_area(attribute, with: nil, **options)
19
+ scope_form.fill_in_rich_text_area(
20
+ attribute,
21
+ with: with || @model.public_send(attribute),
22
+ **options,
23
+ )
24
+ end
25
+
18
26
  def attach_file(attribute, path = nil, **options)
19
27
  scope_form.attach_file(
20
28
  attribute,
@@ -1,18 +1,54 @@
1
+ require "with_form/translation_helpers"
2
+
1
3
  module WithForm
2
4
  class ScopeForm
3
5
  include ActionView::Helpers::TranslationHelper
6
+ include WithForm::TranslationHelpers
4
7
 
5
- delegate :check, :uncheck, :choose, to: :@page
8
+ delegate :choose, to: :@page
6
9
 
7
10
  def initialize(scope:, page:)
8
11
  @page = page
9
12
  @scope = scope
10
13
  end
11
14
 
15
+ def check(attribute, **options)
16
+ case attribute
17
+ when Symbol
18
+ value = label(attribute)
19
+ else
20
+ value = attribute
21
+ end
22
+
23
+ @page.check value, **options
24
+ end
25
+
26
+ def uncheck(attribute, **options)
27
+ case attribute
28
+ when Symbol
29
+ value = label(attribute)
30
+ else
31
+ value = attribute
32
+ end
33
+
34
+ @page.uncheck value, **options
35
+ end
36
+
12
37
  def fill_in(attribute, with:, **options)
13
38
  @page.fill_in label(attribute), with: with, **options
14
39
  end
15
40
 
41
+ def fill_in_rich_text_area(locator = nil, with:)
42
+ fill_in_script = <<~JS
43
+ this.editor.loadHTML(arguments[0])
44
+ JS
45
+
46
+ @page.find(:rich_text_area, label(locator)).execute_script(
47
+ fill_in_script,
48
+ with.to_s,
49
+ )
50
+ end
51
+
16
52
  def select(value, from:, **options)
17
53
  @page.select value, from: label(from), **options
18
54
  end
@@ -30,11 +66,11 @@ module WithForm
30
66
  end
31
67
 
32
68
  def submit(action = nil)
33
- translate(action || :submit, scope: [:helpers, :submit, @scope])
69
+ super(@scope, action || :submit)
34
70
  end
35
71
 
36
72
  def label(attribute)
37
- translate(attribute, scope: [:helpers, :label, @scope])
73
+ super(@scope, attribute)
38
74
  end
39
75
  end
40
76
  end
@@ -0,0 +1,11 @@
1
+ module WithForm
2
+ module TranslationHelpers
3
+ def submit(model_name, action = :submit)
4
+ translate(action, scope: [:helpers, :submit, model_name])
5
+ end
6
+
7
+ def label(model_name, attribute)
8
+ translate(attribute, scope: [:helpers, :label, model_name])
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module WithForm
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
metadata CHANGED
@@ -1,35 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: with_form
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Doyle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-10 00:00:00.000000000 Z
11
+ date: 2020-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: railties
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 6.0.2
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
- version: 6.0.2.1
19
+ version: 5.2.0
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: 6.0.2
30
24
  - - ">="
31
25
  - !ruby/object:Gem::Version
32
- version: 6.0.2.1
26
+ version: 5.2.0
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: capybara
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,6 +52,20 @@ dependencies:
58
52
  - - ">="
59
53
  - !ruby/object:Gem::Version
60
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activerecord
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 5.2.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 5.2.0
61
69
  - !ruby/object:Gem::Dependency
62
70
  name: selenium-webdriver
63
71
  requirement: !ruby/object:Gem::Requirement
@@ -104,6 +112,7 @@ files:
104
112
  - lib/with_form/model_form.rb
105
113
  - lib/with_form/scope_form.rb
106
114
  - lib/with_form/test_helpers.rb
115
+ - lib/with_form/translation_helpers.rb
107
116
  - lib/with_form/version.rb
108
117
  homepage: https://github.com/seanpdoyle/with_form
109
118
  licenses: